code cleanup
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Has been cancelled
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Has been cancelled
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Has been cancelled
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Has been cancelled
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Has been cancelled
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Has been cancelled
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Has been cancelled
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Has been cancelled
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(system_control)
|
||||
return()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 326 B |
Binary file not shown.
|
Before Width: | Height: | Size: 511 B |
Binary file not shown.
|
Before Width: | Height: | Size: 815 B |
Binary file not shown.
@@ -1,8 +1,10 @@
|
||||
idf_component_register(SRCS
|
||||
src/ble/ble_connection.c
|
||||
src/ble/ble_scanner.c
|
||||
src/ble_manager.c
|
||||
src/wifi_manager.c
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
REQUIRES
|
||||
bt
|
||||
driver
|
||||
nvs_flash
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: '>=4.1.0'
|
||||
|
||||
espressif/ble_conn_mgr: '^0.1.3'
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "ble_device.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
void ble_connect(device_info_t *device);
|
||||
void ble_clear_bonds(void);
|
||||
void ble_clear_bond(const ble_addr_t *addr);
|
||||
void read_characteristic(uint16_t char_val_handle);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <host/ble_hs.h>
|
||||
#include <host/util/util.h>
|
||||
#include <nimble/nimble_port.h>
|
||||
#include <nimble/nimble_port_freertos.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Structure to cache device data
|
||||
typedef struct
|
||||
{
|
||||
ble_addr_t addr;
|
||||
uint16_t manufacturer_id;
|
||||
uint8_t manufacturer_data[31]; // Max. length of manufacturer data
|
||||
uint8_t manufacturer_data_len;
|
||||
char name[32];
|
||||
uint16_t service_uuids_16[10]; // Up to 10 16-bit Service UUIDs
|
||||
uint8_t service_uuids_16_count;
|
||||
ble_uuid128_t service_uuids_128[5]; // Up to 5 128-bit Service UUIDs
|
||||
uint8_t service_uuids_128_count;
|
||||
bool has_manufacturer;
|
||||
bool has_name;
|
||||
int8_t rssi;
|
||||
} device_info_t;
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "ble_device.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
void start_scan(void);
|
||||
int get_device_count(void);
|
||||
device_info_t *get_device(int index);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
void ble_manager_task(void *pvParameter);
|
||||
void ble_connect_to_device(int index);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
#include "ble/ble_connection.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <host/ble_gap.h>
|
||||
#include <host/ble_hs.h>
|
||||
#include <host/ble_sm.h>
|
||||
#include <host/ble_store.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "ble_connection";
|
||||
|
||||
static uint16_t g_conn_handle;
|
||||
static uint16_t g_char_val_handle; // Handle der Characteristic, die du lesen willst
|
||||
static bool g_bonding_in_progress = false;
|
||||
|
||||
const char *ble_error_to_string(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case 0:
|
||||
return "Success";
|
||||
case BLE_HS_EDONE:
|
||||
return "Operation complete";
|
||||
case BLE_HS_EALREADY:
|
||||
return "Operation already in progress";
|
||||
case BLE_HS_EINVAL:
|
||||
return "Invalid argument";
|
||||
case BLE_HS_EMSGSIZE:
|
||||
return "Message too large";
|
||||
case BLE_HS_ENOENT:
|
||||
return "No entry found";
|
||||
case BLE_HS_ENOMEM:
|
||||
return "Out of memory";
|
||||
case BLE_HS_ENOTCONN:
|
||||
return "Not connected";
|
||||
case BLE_HS_ENOTSUP:
|
||||
return "Not supported";
|
||||
case BLE_HS_EAPP:
|
||||
return "Application error";
|
||||
case BLE_HS_EBADDATA:
|
||||
return "Bad data";
|
||||
case BLE_HS_EOS:
|
||||
return "OS error";
|
||||
case BLE_HS_ECONTROLLER:
|
||||
return "Controller error";
|
||||
case BLE_HS_ETIMEOUT:
|
||||
return "Timeout";
|
||||
case BLE_HS_EBUSY:
|
||||
return "Busy";
|
||||
case BLE_HS_EREJECT:
|
||||
return "Rejected";
|
||||
case BLE_HS_EUNKNOWN:
|
||||
return "Unknown error";
|
||||
case BLE_HS_EROLE:
|
||||
return "Role error";
|
||||
case BLE_HS_ETIMEOUT_HCI:
|
||||
return "HCI timeout";
|
||||
case BLE_HS_ENOMEM_EVT:
|
||||
return "No memory for event";
|
||||
case BLE_HS_ENOADDR:
|
||||
return "No address";
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
return "Not synchronized";
|
||||
case BLE_HS_EAUTHEN:
|
||||
return "Authentication failed";
|
||||
case BLE_HS_EAUTHOR:
|
||||
return "Authorization failed";
|
||||
case BLE_HS_EENCRYPT:
|
||||
return "Encryption failed";
|
||||
case BLE_HS_EENCRYPT_KEY_SZ:
|
||||
return "Encryption key size";
|
||||
case BLE_HS_ESTORE_CAP:
|
||||
return "Storage capacity exceeded";
|
||||
case BLE_HS_ESTORE_FAIL:
|
||||
return "Storage failure";
|
||||
default:
|
||||
// ATT-Fehler prüfen
|
||||
if ((status & 0x100) == 0x100)
|
||||
{
|
||||
return "ATT error";
|
||||
}
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
static void ble_sm_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(TAG, "Passkey action required");
|
||||
// Hier können Sie Passkey-Aktionen implementieren
|
||||
// z.B. Display passkey, Input passkey, etc.
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
ESP_LOGI(TAG, "Encryption change: status=%d", event->enc_change.status);
|
||||
if (event->enc_change.status == 0)
|
||||
{
|
||||
ESP_LOGI(TAG, "Encryption established successfully");
|
||||
g_bonding_in_progress = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
||||
ESP_LOGI(TAG, "Repeat pairing");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_device_bonded(const ble_addr_t *addr)
|
||||
{
|
||||
struct ble_store_value_sec sec_value;
|
||||
struct ble_store_key_sec sec_key = {0};
|
||||
|
||||
sec_key.peer_addr = *addr;
|
||||
sec_key.idx = 0;
|
||||
|
||||
int rc = ble_store_read_peer_sec(&sec_key, &sec_value);
|
||||
return (rc == 0);
|
||||
}
|
||||
|
||||
static void initiate_bonding(uint16_t conn_handle)
|
||||
{
|
||||
if (!g_bonding_in_progress)
|
||||
{
|
||||
g_bonding_in_progress = true;
|
||||
ESP_LOGI(TAG, "Initiating bonding for connection %d", conn_handle);
|
||||
|
||||
// Starte Security/Bonding Prozess
|
||||
int rc = ble_gap_security_initiate(conn_handle);
|
||||
if (rc != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to initiate security: %s", ble_error_to_string(rc));
|
||||
g_bonding_in_progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gattc_svcs_callback(uint16_t conn_handle, const struct ble_gatt_error *error,
|
||||
const struct ble_gatt_svc *service, void *arg)
|
||||
{
|
||||
if (error->status != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error discovering service: %s", ble_error_to_string(error->status));
|
||||
return 0;
|
||||
}
|
||||
|
||||
char uuid_str[37]; // Maximale Länge für 128-bit UUID
|
||||
ble_uuid_to_str(&service->uuid.u, uuid_str);
|
||||
ESP_LOGI(TAG, "Discovered service: %s", uuid_str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gattc_char_callback(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *chr,
|
||||
void *arg)
|
||||
{
|
||||
if (error->status != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error discovering characteristic: %d", error->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_char_val_handle = chr->val_handle;
|
||||
read_characteristic(chr->val_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Callback für GATT-Events
|
||||
static int gattc_event_callback(uint16_t conn_handle, const struct ble_gatt_error *error,
|
||||
const struct ble_gatt_svc *service, void *arg)
|
||||
{
|
||||
if (error->status != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error discovering service: %d", error->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ble_gattc_disc_all_svcs(conn_handle, gattc_svcs_callback, NULL);
|
||||
// ble_gattc_disc_all_chrs(conn_handle, service->start_handle, service->end_handle, gattc_char_callback, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gattc_read_callback(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr,
|
||||
void *arg)
|
||||
{
|
||||
if (error->status == 0)
|
||||
{
|
||||
ESP_LOGI(TAG, "Wert gelesen %d, Länge: %d", attr->handle, attr->om->om_len);
|
||||
ESP_LOG_BUFFER_HEX("READ_DATA", attr->om->om_data, attr->om->om_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Lesefehler, Status: %d", error->status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ble_gap_event_handler(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
device_info_t *device = (device_info_t *)arg;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
if (event->connect.status == 0)
|
||||
{
|
||||
g_conn_handle = event->connect.conn_handle;
|
||||
ESP_LOGI(TAG, "Connected; conn_handle=%d", g_conn_handle);
|
||||
|
||||
// Prüfe ob Device bereits gebondet ist
|
||||
if (is_device_bonded(&device->addr))
|
||||
{
|
||||
ESP_LOGI(TAG, "Device already bonded, using existing bond");
|
||||
// Bei gebondetem Device kann direkt mit Service Discovery begonnen werden
|
||||
ble_gattc_disc_all_svcs(g_conn_handle, gattc_event_callback, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Device not bonded, initiating bonding");
|
||||
// Starte Bonding-Prozess
|
||||
initiate_bonding(g_conn_handle);
|
||||
// Service Discovery wird nach erfolgreichem Bonding gestartet
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Connection failed; status=%d", event->connect.status);
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
g_conn_handle = 0;
|
||||
g_bonding_in_progress = false;
|
||||
ESP_LOGI(TAG, "Disconnected; reason=%d", event->disconnect.reason);
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
ESP_LOGI(TAG, "Connection updated; status=%d", event->conn_update.status);
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
ESP_LOGI(TAG, "Encryption change: status=%d", event->enc_change.status);
|
||||
if (event->enc_change.status == 0)
|
||||
{
|
||||
ESP_LOGI(TAG, "Encryption established, bonding complete");
|
||||
g_bonding_in_progress = false;
|
||||
// Nach erfolgreichem Bonding: Service Discovery starten
|
||||
ble_gattc_disc_all_svcs(g_conn_handle, gattc_event_callback, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Encryption failed: %s", ble_error_to_string(event->enc_change.status));
|
||||
g_bonding_in_progress = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(TAG, "Passkey action event");
|
||||
// Implementieren Sie hier die Passkey-Behandlung
|
||||
// z.B. einen festen Passkey eingeben:
|
||||
struct ble_sm_io pkey = {0};
|
||||
pkey.action = BLE_SM_IOACT_INPUT;
|
||||
pkey.passkey = 100779;
|
||||
ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
||||
ESP_LOGI(TAG, "Device requests repeat pairing");
|
||||
|
||||
// Hole die Peer-Adresse aus der Verbindung
|
||||
struct ble_gap_conn_desc conn_desc;
|
||||
int rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &conn_desc);
|
||||
if (rc == 0)
|
||||
{
|
||||
// Lösche alte Bonding-Info
|
||||
ble_clear_bond(&conn_desc.peer_ota_addr);
|
||||
}
|
||||
|
||||
// Erlaube erneutes Pairing
|
||||
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ble_connect(device_info_t *device)
|
||||
{
|
||||
struct ble_gap_conn_params conn_params = {
|
||||
.scan_itvl = 0x0010,
|
||||
.scan_window = 0x0010,
|
||||
.itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN,
|
||||
.itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX,
|
||||
.latency = BLE_GAP_INITIAL_CONN_LATENCY,
|
||||
.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT,
|
||||
.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN,
|
||||
.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN,
|
||||
};
|
||||
|
||||
// Prüfe ob Device bereits gebondet ist
|
||||
if (is_device_bonded(&device->addr))
|
||||
{
|
||||
ESP_LOGI(TAG, "Connecting to bonded device");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Connecting to new device (will bond after connection)");
|
||||
}
|
||||
|
||||
int rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &device->addr, 30000, &conn_params, ble_gap_event_handler, device);
|
||||
if (rc != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error initiating connection: %s", ble_error_to_string(rc));
|
||||
}
|
||||
}
|
||||
|
||||
void ble_clear_bonds(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Clearing all bonds");
|
||||
|
||||
int rc = ble_store_clear();
|
||||
if (rc != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to clear bond storage: %s", ble_error_to_string(rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "All bonds cleared successfully");
|
||||
}
|
||||
}
|
||||
|
||||
// Funktion zum Löschen der Bonding-Info eines bestimmten Devices
|
||||
void ble_clear_bond(const ble_addr_t *addr)
|
||||
{
|
||||
ESP_LOGI(TAG, "Clearing bond for specific device");
|
||||
|
||||
int rc = ble_store_util_delete_peer(addr);
|
||||
if (rc != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to delete peer: %s", ble_error_to_string(rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Peer deleted successfully");
|
||||
}
|
||||
}
|
||||
|
||||
void read_characteristic(uint16_t char_val_handle)
|
||||
{
|
||||
if (char_val_handle != 0 && g_conn_handle != 0)
|
||||
{
|
||||
int rc = ble_gattc_read(g_conn_handle, char_val_handle, gattc_read_callback, NULL);
|
||||
if (rc != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error reading characteristic: %d", rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
#include "ble/ble_scanner.h"
|
||||
|
||||
#include "ble/ble_device.h"
|
||||
#include "led_status.h"
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <host/ble_hs.h>
|
||||
#include <host/util/util.h>
|
||||
#include <nimble/nimble_port.h>
|
||||
#include <nimble/nimble_port_freertos.h>
|
||||
#include <services/gap/ble_svc_gap.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const char *TAG = "ble_scanner";
|
||||
|
||||
// List of allowed manufacturer IDs
|
||||
static const uint16_t ALLOWED_MANUFACTURERS[] = {
|
||||
0xC0DE, // mars3142
|
||||
};
|
||||
static const size_t NUM_MANUFACTURERS = sizeof(ALLOWED_MANUFACTURERS) / sizeof(uint16_t);
|
||||
static bool scanning = false;
|
||||
|
||||
static bool is_manufacturer_allowed(uint16_t company_id)
|
||||
{
|
||||
for (size_t i = 0; i < NUM_MANUFACTURERS; i++)
|
||||
{
|
||||
if (ALLOWED_MANUFACTURERS[i] == company_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ble_central_gap_event(struct ble_gap_event *event, void *arg);
|
||||
|
||||
/**
|
||||
* Starts the BLE scan process.
|
||||
*/
|
||||
void start_scan(void)
|
||||
{
|
||||
led_behavior_t led_behavior = {
|
||||
.on_time_ms = 200,
|
||||
.off_time_ms = 200,
|
||||
.color = {.red = 0, .green = 0, .blue = 50},
|
||||
.index = 1,
|
||||
.mode = LED_MODE_BLINK,
|
||||
};
|
||||
led_status_set_behavior(led_behavior);
|
||||
|
||||
struct ble_gap_disc_params disc_params = {
|
||||
.filter_policy = 0,
|
||||
.limited = 0,
|
||||
.passive = 0,
|
||||
.filter_duplicates = 1,
|
||||
};
|
||||
|
||||
int32_t duration_ms = 10000; // 10 seconds
|
||||
|
||||
int rc = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, duration_ms, &disc_params, ble_central_gap_event, NULL);
|
||||
if (rc != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error starting scan; rc=%d", rc);
|
||||
}
|
||||
scanning = true;
|
||||
}
|
||||
|
||||
#define MAX_DEVICES 40
|
||||
static device_info_t devices[MAX_DEVICES];
|
||||
static int device_count = 0;
|
||||
|
||||
// Helper function to find or create a device entry
|
||||
static device_info_t *find_or_create_device(const ble_addr_t *addr)
|
||||
{
|
||||
// Search for existing device
|
||||
for (int i = 0; i < device_count; i++)
|
||||
{
|
||||
if (memcmp(&devices[i].addr, addr, sizeof(ble_addr_t)) == 0)
|
||||
{
|
||||
return &devices[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Add new device
|
||||
if (device_count < MAX_DEVICES)
|
||||
{
|
||||
memset(&devices[device_count], 0, sizeof(device_info_t));
|
||||
memcpy(&devices[device_count].addr, addr, sizeof(ble_addr_t));
|
||||
devices[device_count].has_manufacturer = false;
|
||||
devices[device_count].has_name = false;
|
||||
strcpy(devices[device_count].name, "Unknown");
|
||||
return &devices[device_count++];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ble_central_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_disc_desc *disc;
|
||||
struct ble_hs_adv_fields fields;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case BLE_GAP_EVENT_DISC: {
|
||||
disc = &event->disc;
|
||||
|
||||
// Find or create device
|
||||
device_info_t *device = find_or_create_device(&disc->addr);
|
||||
if (device == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Update RSSI
|
||||
device->rssi = disc->rssi;
|
||||
|
||||
// Parse advertising data
|
||||
memset(&fields, 0, sizeof(fields));
|
||||
ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
|
||||
|
||||
// Process manufacturer data
|
||||
if (fields.mfg_data != NULL && fields.mfg_data_len >= 2)
|
||||
{
|
||||
uint16_t company_id = fields.mfg_data[0] | (fields.mfg_data[1] << 8);
|
||||
device->manufacturer_id = company_id;
|
||||
device->has_manufacturer = true;
|
||||
|
||||
// Store complete manufacturer data (incl. Company ID)
|
||||
device->manufacturer_data_len = fields.mfg_data_len;
|
||||
memcpy(device->manufacturer_data, fields.mfg_data, fields.mfg_data_len);
|
||||
}
|
||||
|
||||
// Process name
|
||||
if (fields.name != NULL && fields.name_len > 0)
|
||||
{
|
||||
size_t copy_len = fields.name_len < sizeof(device->name) - 1 ? fields.name_len : sizeof(device->name) - 1;
|
||||
memcpy(device->name, fields.name, copy_len);
|
||||
device->name[copy_len] = '\0';
|
||||
device->has_name = true;
|
||||
}
|
||||
|
||||
// Process 16-bit Service UUIDs
|
||||
if (fields.uuids16 != NULL && fields.num_uuids16 > 0)
|
||||
{
|
||||
for (int i = 0; i < fields.num_uuids16 && device->service_uuids_16_count < 10; i++)
|
||||
{
|
||||
// Check if UUID already exists
|
||||
bool exists = false;
|
||||
for (int j = 0; j < device->service_uuids_16_count; j++)
|
||||
{
|
||||
if (device->service_uuids_16[j] == fields.uuids16[i].value)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
device->service_uuids_16[device->service_uuids_16_count++] = fields.uuids16[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process 128-bit Service UUIDs
|
||||
if (fields.uuids128 != NULL && fields.num_uuids128 > 0)
|
||||
{
|
||||
for (int i = 0; i < fields.num_uuids128 && device->service_uuids_128_count < 5; i++)
|
||||
{
|
||||
// Check if UUID already exists
|
||||
bool exists = false;
|
||||
for (int j = 0; j < device->service_uuids_128_count; j++)
|
||||
{
|
||||
if (memcmp(&device->service_uuids_128[j], &fields.uuids128[i], sizeof(ble_uuid128_t)) == 0)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
memcpy(&device->service_uuids_128[device->service_uuids_128_count++], &fields.uuids128[i],
|
||||
sizeof(ble_uuid128_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have all data and the device is allowed
|
||||
if (device->has_name && device->has_manufacturer && is_manufacturer_allowed(device->manufacturer_id))
|
||||
{
|
||||
ESP_LOGI(TAG, "*** Allowed device found ***");
|
||||
ESP_LOGI(TAG, " Name: %s", device->name);
|
||||
ESP_LOGI(TAG, " Address: %02X:%02X:%02X:%02X:%02X:%02X", device->addr.val[5], device->addr.val[4],
|
||||
device->addr.val[3], device->addr.val[2], device->addr.val[1], device->addr.val[0]);
|
||||
ESP_LOGI(TAG, " Manufacturer ID: 0x%04X", device->manufacturer_id);
|
||||
ESP_LOGI(TAG, " RSSI: %d dBm", device->rssi);
|
||||
|
||||
// Print Service UUIDs
|
||||
if (device->service_uuids_16_count > 0)
|
||||
{
|
||||
ESP_LOGI(TAG, " 16-bit Service UUIDs (%d):", device->service_uuids_16_count);
|
||||
for (int i = 0; i < device->service_uuids_16_count; i++)
|
||||
{
|
||||
const char *name = "";
|
||||
// Known Service UUIDs
|
||||
switch (device->service_uuids_16[i])
|
||||
{
|
||||
case 0x180A:
|
||||
name = " (Device Information)";
|
||||
break;
|
||||
case 0x180F:
|
||||
name = " (Battery Service)";
|
||||
break;
|
||||
case 0x1801:
|
||||
name = " (Generic Attribute)";
|
||||
break;
|
||||
case 0x1800:
|
||||
name = " (Generic Access)";
|
||||
break;
|
||||
case 0x181A:
|
||||
name = " (Environmental Sensing)";
|
||||
break;
|
||||
default:
|
||||
if (device->service_uuids_16[i] >= 0xA000)
|
||||
{
|
||||
name = " (Custom)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, " - 0x%04X%s", device->service_uuids_16[i], name);
|
||||
}
|
||||
}
|
||||
|
||||
if (device->service_uuids_128_count > 0)
|
||||
{
|
||||
ESP_LOGI(TAG, " 128-bit Service UUIDs (%d):", device->service_uuids_128_count);
|
||||
for (int i = 0; i < device->service_uuids_128_count; i++)
|
||||
{
|
||||
char uuid_str[37]; // UUID string format
|
||||
snprintf(uuid_str, sizeof(uuid_str),
|
||||
"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
device->service_uuids_128[i].value[15], device->service_uuids_128[i].value[14],
|
||||
device->service_uuids_128[i].value[13], device->service_uuids_128[i].value[12],
|
||||
device->service_uuids_128[i].value[11], device->service_uuids_128[i].value[10],
|
||||
device->service_uuids_128[i].value[9], device->service_uuids_128[i].value[8],
|
||||
device->service_uuids_128[i].value[7], device->service_uuids_128[i].value[6],
|
||||
device->service_uuids_128[i].value[5], device->service_uuids_128[i].value[4],
|
||||
device->service_uuids_128[i].value[3], device->service_uuids_128[i].value[2],
|
||||
device->service_uuids_128[i].value[1], device->service_uuids_128[i].value[0]);
|
||||
ESP_LOGI(TAG, " - %s", uuid_str);
|
||||
}
|
||||
}
|
||||
|
||||
// Print manufacturer data (without Company ID, i.e., from byte 2)
|
||||
if (device->manufacturer_data_len > 2)
|
||||
{
|
||||
int payload_len = device->manufacturer_data_len - 2;
|
||||
ESP_LOGI(TAG, " Manufacturer Data (%d bytes):", payload_len);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, &device->manufacturer_data[2], payload_len, ESP_LOG_INFO);
|
||||
|
||||
// Print data byte by byte for better readability
|
||||
ESP_LOGI(TAG, " Data interpretation:");
|
||||
for (int i = 0; i < payload_len; i++)
|
||||
{
|
||||
ESP_LOGI(TAG, " - Byte %d: 0x%02X (%d)", i, device->manufacturer_data[i + 2],
|
||||
device->manufacturer_data[i + 2]);
|
||||
}
|
||||
|
||||
// Optional: Interpret data as 16-bit values (if the number of bytes is even)
|
||||
if (payload_len >= 2 && (payload_len % 2 == 0))
|
||||
{
|
||||
ESP_LOGI(TAG, " As 16-bit values:");
|
||||
for (int i = 0; i < payload_len; i += 2)
|
||||
{
|
||||
uint16_t value = device->manufacturer_data[i + 2] | (device->manufacturer_data[i + 3] << 8);
|
||||
ESP_LOGI(TAG, " - Word %d: 0x%04X (%d)", i / 2, value, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, " No manufacturer payload data");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_DISC_COMPLETE: {
|
||||
led_behavior_t led_behavior = {
|
||||
.index = 1,
|
||||
.mode = LED_MODE_OFF,
|
||||
};
|
||||
led_status_set_behavior(led_behavior);
|
||||
|
||||
ESP_LOGI(TAG, "Discovery complete");
|
||||
scanning = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_info_t *get_device(int index)
|
||||
{
|
||||
if (index < 0 || index >= device_count || !is_manufacturer_allowed(devices[index].manufacturer_id))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &devices[index];
|
||||
}
|
||||
|
||||
int get_device_count(void)
|
||||
{
|
||||
if (!scanning)
|
||||
{
|
||||
return device_count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,312 +1,23 @@
|
||||
#include "ble_manager.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
|
||||
#include "ble/ble_connection.h"
|
||||
#include "ble/ble_scanner.h"
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <host/ble_hs.h>
|
||||
#include <host/util/util.h>
|
||||
#include <nimble/nimble_port.h>
|
||||
#include <nimble/nimble_port_freertos.h>
|
||||
#include <services/gap/ble_svc_gap.h>
|
||||
|
||||
static const char *TAG = "ble_manager";
|
||||
|
||||
// List of allowed manufacturer IDs
|
||||
static const uint16_t ALLOWED_MANUFACTURERS[] = {
|
||||
0xC0DE, // mars3142
|
||||
};
|
||||
static const size_t NUM_MANUFACTURERS = sizeof(ALLOWED_MANUFACTURERS) / sizeof(uint16_t);
|
||||
|
||||
// Structure to cache device data
|
||||
typedef struct
|
||||
{
|
||||
ble_addr_t addr;
|
||||
uint16_t manufacturer_id;
|
||||
uint8_t manufacturer_data[31]; // Max. length of manufacturer data
|
||||
uint8_t manufacturer_data_len;
|
||||
char name[32];
|
||||
uint16_t service_uuids_16[10]; // Up to 10 16-bit Service UUIDs
|
||||
uint8_t service_uuids_16_count;
|
||||
ble_uuid128_t service_uuids_128[5]; // Up to 5 128-bit Service UUIDs
|
||||
uint8_t service_uuids_128_count;
|
||||
bool has_manufacturer;
|
||||
bool has_name;
|
||||
int8_t rssi;
|
||||
} device_info_t;
|
||||
|
||||
static bool is_manufacturer_allowed(uint16_t company_id)
|
||||
{
|
||||
for (size_t i = 0; i < NUM_MANUFACTURERS; i++)
|
||||
{
|
||||
if (ALLOWED_MANUFACTURERS[i] == company_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ble_central_gap_event(struct ble_gap_event *event, void *arg);
|
||||
|
||||
/**
|
||||
* Starts the BLE scan process.
|
||||
*/
|
||||
static void start_scan(void)
|
||||
{
|
||||
struct ble_gap_disc_params disc_params = {
|
||||
.filter_policy = 0,
|
||||
.limited = 0,
|
||||
.passive = 0,
|
||||
.filter_duplicates = 1,
|
||||
};
|
||||
|
||||
int rc = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, &disc_params, ble_central_gap_event, NULL);
|
||||
if (rc != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error starting scan; rc=%d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_DEVICES 10
|
||||
static device_info_t devices[MAX_DEVICES];
|
||||
static int device_count = 0;
|
||||
|
||||
// Helper function to find or create a device entry
|
||||
static device_info_t *find_or_create_device(const ble_addr_t *addr)
|
||||
{
|
||||
// Search for existing device
|
||||
for (int i = 0; i < device_count; i++)
|
||||
{
|
||||
if (memcmp(&devices[i].addr, addr, sizeof(ble_addr_t)) == 0)
|
||||
{
|
||||
return &devices[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Add new device
|
||||
if (device_count < MAX_DEVICES)
|
||||
{
|
||||
memset(&devices[device_count], 0, sizeof(device_info_t));
|
||||
memcpy(&devices[device_count].addr, addr, sizeof(ble_addr_t));
|
||||
devices[device_count].has_manufacturer = false;
|
||||
devices[device_count].has_name = false;
|
||||
strcpy(devices[device_count].name, "Unknown");
|
||||
return &devices[device_count++];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ble_central_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_disc_desc *disc;
|
||||
struct ble_hs_adv_fields fields;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case BLE_GAP_EVENT_DISC: {
|
||||
disc = &event->disc;
|
||||
|
||||
// Find or create device
|
||||
device_info_t *device = find_or_create_device(&disc->addr);
|
||||
if (device == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Update RSSI
|
||||
device->rssi = disc->rssi;
|
||||
|
||||
// Parse advertising data
|
||||
memset(&fields, 0, sizeof(fields));
|
||||
ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
|
||||
|
||||
// Process manufacturer data
|
||||
if (fields.mfg_data != NULL && fields.mfg_data_len >= 2)
|
||||
{
|
||||
uint16_t company_id = fields.mfg_data[0] | (fields.mfg_data[1] << 8);
|
||||
device->manufacturer_id = company_id;
|
||||
device->has_manufacturer = true;
|
||||
|
||||
// Store complete manufacturer data (incl. Company ID)
|
||||
device->manufacturer_data_len = fields.mfg_data_len;
|
||||
memcpy(device->manufacturer_data, fields.mfg_data, fields.mfg_data_len);
|
||||
}
|
||||
|
||||
// Process name
|
||||
if (fields.name != NULL && fields.name_len > 0)
|
||||
{
|
||||
size_t copy_len = fields.name_len < sizeof(device->name) - 1 ? fields.name_len : sizeof(device->name) - 1;
|
||||
memcpy(device->name, fields.name, copy_len);
|
||||
device->name[copy_len] = '\0';
|
||||
device->has_name = true;
|
||||
}
|
||||
|
||||
// Process 16-bit Service UUIDs
|
||||
if (fields.uuids16 != NULL && fields.num_uuids16 > 0)
|
||||
{
|
||||
for (int i = 0; i < fields.num_uuids16 && device->service_uuids_16_count < 10; i++)
|
||||
{
|
||||
// Check if UUID already exists
|
||||
bool exists = false;
|
||||
for (int j = 0; j < device->service_uuids_16_count; j++)
|
||||
{
|
||||
if (device->service_uuids_16[j] == fields.uuids16[i].value)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
device->service_uuids_16[device->service_uuids_16_count++] = fields.uuids16[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process 128-bit Service UUIDs
|
||||
if (fields.uuids128 != NULL && fields.num_uuids128 > 0)
|
||||
{
|
||||
for (int i = 0; i < fields.num_uuids128 && device->service_uuids_128_count < 5; i++)
|
||||
{
|
||||
// Check if UUID already exists
|
||||
bool exists = false;
|
||||
for (int j = 0; j < device->service_uuids_128_count; j++)
|
||||
{
|
||||
if (memcmp(&device->service_uuids_128[j], &fields.uuids128[i], sizeof(ble_uuid128_t)) == 0)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
memcpy(&device->service_uuids_128[device->service_uuids_128_count++], &fields.uuids128[i],
|
||||
sizeof(ble_uuid128_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have all data and the device is allowed
|
||||
if (device->has_manufacturer && is_manufacturer_allowed(device->manufacturer_id))
|
||||
{
|
||||
ESP_LOGI(TAG, "*** Allowed device found ***");
|
||||
ESP_LOGI(TAG, " Name: %s", device->name);
|
||||
ESP_LOGI(TAG, " Address: %02X:%02X:%02X:%02X:%02X:%02X", device->addr.val[5], device->addr.val[4],
|
||||
device->addr.val[3], device->addr.val[2], device->addr.val[1], device->addr.val[0]);
|
||||
ESP_LOGI(TAG, " Manufacturer ID: 0x%04X", device->manufacturer_id);
|
||||
ESP_LOGI(TAG, " RSSI: %d dBm", device->rssi);
|
||||
|
||||
// Print Service UUIDs
|
||||
if (device->service_uuids_16_count > 0)
|
||||
{
|
||||
ESP_LOGI(TAG, " 16-bit Service UUIDs (%d):", device->service_uuids_16_count);
|
||||
for (int i = 0; i < device->service_uuids_16_count; i++)
|
||||
{
|
||||
const char *name = "";
|
||||
// Known Service UUIDs
|
||||
switch (device->service_uuids_16[i])
|
||||
{
|
||||
case 0x180A:
|
||||
name = " (Device Information)";
|
||||
break;
|
||||
case 0x180F:
|
||||
name = " (Battery Service)";
|
||||
break;
|
||||
case 0x1801:
|
||||
name = " (Generic Attribute)";
|
||||
break;
|
||||
case 0x1800:
|
||||
name = " (Generic Access)";
|
||||
break;
|
||||
case 0x181A:
|
||||
name = " (Environmental Sensing)";
|
||||
break;
|
||||
default:
|
||||
if (device->service_uuids_16[i] >= 0xA000)
|
||||
{
|
||||
name = " (Custom)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, " - 0x%04X%s", device->service_uuids_16[i], name);
|
||||
}
|
||||
}
|
||||
|
||||
if (device->service_uuids_128_count > 0)
|
||||
{
|
||||
ESP_LOGI(TAG, " 128-bit Service UUIDs (%d):", device->service_uuids_128_count);
|
||||
for (int i = 0; i < device->service_uuids_128_count; i++)
|
||||
{
|
||||
char uuid_str[37]; // UUID string format
|
||||
snprintf(uuid_str, sizeof(uuid_str),
|
||||
"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
device->service_uuids_128[i].value[15], device->service_uuids_128[i].value[14],
|
||||
device->service_uuids_128[i].value[13], device->service_uuids_128[i].value[12],
|
||||
device->service_uuids_128[i].value[11], device->service_uuids_128[i].value[10],
|
||||
device->service_uuids_128[i].value[9], device->service_uuids_128[i].value[8],
|
||||
device->service_uuids_128[i].value[7], device->service_uuids_128[i].value[6],
|
||||
device->service_uuids_128[i].value[5], device->service_uuids_128[i].value[4],
|
||||
device->service_uuids_128[i].value[3], device->service_uuids_128[i].value[2],
|
||||
device->service_uuids_128[i].value[1], device->service_uuids_128[i].value[0]);
|
||||
ESP_LOGI(TAG, " - %s", uuid_str);
|
||||
}
|
||||
}
|
||||
|
||||
// Print manufacturer data (without Company ID, i.e., from byte 2)
|
||||
if (device->manufacturer_data_len > 2)
|
||||
{
|
||||
int payload_len = device->manufacturer_data_len - 2;
|
||||
ESP_LOGI(TAG, " Manufacturer Data (%d bytes):", payload_len);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, &device->manufacturer_data[2], payload_len, ESP_LOG_INFO);
|
||||
|
||||
// Print data byte by byte for better readability
|
||||
ESP_LOGI(TAG, " Data interpretation:");
|
||||
for (int i = 0; i < payload_len; i++)
|
||||
{
|
||||
ESP_LOGI(TAG, " - Byte %d: 0x%02X (%d)", i, device->manufacturer_data[i + 2],
|
||||
device->manufacturer_data[i + 2]);
|
||||
}
|
||||
|
||||
// Optional: Interpret data as 16-bit values (if the number of bytes is even)
|
||||
if (payload_len >= 2 && (payload_len % 2 == 0))
|
||||
{
|
||||
ESP_LOGI(TAG, " As 16-bit values:");
|
||||
for (int i = 0; i < payload_len; i += 2)
|
||||
{
|
||||
uint16_t value = device->manufacturer_data[i + 2] | (device->manufacturer_data[i + 3] << 8);
|
||||
ESP_LOGI(TAG, " - Word %d: 0x%04X (%d)", i / 2, value, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, " No manufacturer payload data");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_DISC_COMPLETE: {
|
||||
ESP_LOGI(TAG, "Discovery complete, restarting scan...");
|
||||
// Optional: Reset device list
|
||||
device_count = 0;
|
||||
start_scan();
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that is called when the NimBLE stack is synchronized and ready.
|
||||
*/
|
||||
static void on_sync(void)
|
||||
{
|
||||
// The stack is ready, we can start the scan.
|
||||
start_scan();
|
||||
}
|
||||
|
||||
@@ -333,3 +44,17 @@ void ble_manager_task(void *pvParameter)
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
bool ble_found_devices(void)
|
||||
{
|
||||
return get_device_count() > 0;
|
||||
}
|
||||
|
||||
void ble_connect_to_device(int index)
|
||||
{
|
||||
device_info_t *device = get_device(index);
|
||||
if (device != NULL)
|
||||
{
|
||||
ble_connect(device);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
#include "wifi_manager.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_insights.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/task.h"
|
||||
#include "led_status.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <esp_event.h>
|
||||
#include <esp_insights.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <freertos/task.h>
|
||||
#include <led_status.h>
|
||||
#include <lwip/err.h>
|
||||
#include <lwip/sys.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
// Event group to signal when we are connected
|
||||
static EventGroupHandle_t s_wifi_event_group;
|
||||
@@ -30,11 +30,14 @@ static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_
|
||||
#if CONFIG_WIFI_ENABLED
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
||||
{
|
||||
led_behavior_t led0_behavior = {.mode = LED_MODE_BLINK,
|
||||
.color = {.red = 50, .green = 50, .blue = 0},
|
||||
.on_time_ms = 200,
|
||||
.off_time_ms = 200};
|
||||
led_status_set_behavior(0, led0_behavior);
|
||||
led_behavior_t led0_behavior = {
|
||||
.index = 0,
|
||||
.mode = LED_MODE_BLINK,
|
||||
.color = {.red = 50, .green = 50, .blue = 0},
|
||||
.on_time_ms = 200,
|
||||
.off_time_ms = 200,
|
||||
};
|
||||
led_status_set_behavior(led0_behavior);
|
||||
|
||||
esp_wifi_connect();
|
||||
}
|
||||
@@ -42,32 +45,39 @@ static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_
|
||||
{
|
||||
if (s_retry_num < CONFIG_WIFI_CONNECT_RETRIES)
|
||||
{
|
||||
led_behavior_t led0_behavior = {.mode = LED_MODE_BLINK,
|
||||
.color = {.red = 50, .green = 50, .blue = 0},
|
||||
.on_time_ms = 200,
|
||||
.off_time_ms = 200};
|
||||
led_status_set_behavior(0, led0_behavior);
|
||||
led_behavior_t led0_behavior = {
|
||||
.index = 0,
|
||||
.mode = LED_MODE_BLINK,
|
||||
.color = {.red = 50, .green = 50, .blue = 0},
|
||||
.on_time_ms = 200,
|
||||
.off_time_ms = 200,
|
||||
};
|
||||
led_status_set_behavior(led0_behavior);
|
||||
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_DIAG_EVENT(TAG, "Retrying to connect to the AP");
|
||||
return;
|
||||
}
|
||||
led_behavior_t led0_behavior = {.mode = LED_MODE_BLINK,
|
||||
.color = {.red = 50, .green = 0, .blue = 0},
|
||||
.on_time_ms = 1000,
|
||||
.off_time_ms = 500};
|
||||
led_status_set_behavior(0, led0_behavior);
|
||||
led_behavior_t led0_behavior = {
|
||||
.index = 0,
|
||||
.mode = LED_MODE_BLINK,
|
||||
.color = {.red = 50, .green = 0, .blue = 0},
|
||||
.on_time_ms = 1000,
|
||||
.off_time_ms = 500,
|
||||
};
|
||||
led_status_set_behavior(led0_behavior);
|
||||
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
||||
{
|
||||
led_behavior_t led0_behavior = {
|
||||
.index = 0,
|
||||
.mode = LED_MODE_SOLID,
|
||||
.color = {.red = 0, .green = 50, .blue = 0},
|
||||
};
|
||||
led_status_set_behavior(0, led0_behavior);
|
||||
led_status_set_behavior(led0_behavior);
|
||||
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
ESP_DIAG_EVENT(TAG, "Got IP address:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
if (DEFINED ENV{IDF_PATH})
|
||||
return()
|
||||
endif ()
|
||||
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
project(ImGui)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
imgui.cpp
|
||||
imgui_demo.cpp
|
||||
imgui_draw.cpp
|
||||
imgui_widgets.cpp
|
||||
imgui_tables.cpp
|
||||
|
||||
imgui_impl_sdl3.cpp
|
||||
imgui_impl_sdlrenderer3.cpp
|
||||
)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,826 +0,0 @@
|
||||
// dear imgui: Platform Backend for SDL3
|
||||
// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Gamepad support.
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: IME support.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2025-04-22: IME: honor ImGuiPlatformImeData->WantTextInput as an alternative way to call SDL_StartTextInput(), without IME being necessarily visible.
|
||||
// 2025-04-09: Don't attempt to call SDL_CaptureMouse() on drivers where we don't call SDL_GetGlobalMouseState(). (#8561)
|
||||
// 2025-03-30: Update for SDL3 api changes: Revert SDL_GetClipboardText() memory ownership change. (#8530, #7801)
|
||||
// 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set.
|
||||
// 2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468)
|
||||
// 2025-02-26: Only start SDL_CaptureMouse() when mouse is being dragged, to mitigate issues with e.g.Linux debuggers not claiming capture back. (#6410, #3650)
|
||||
// 2025-02-24: Avoid calling SDL_GetGlobalMouseState() when mouse is in relative mode.
|
||||
// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
|
||||
// 2025-02-10: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn handler.
|
||||
// 2025-01-20: Made ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode_Manual) accept an empty array.
|
||||
// 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten.
|
||||
// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807)
|
||||
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
|
||||
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
|
||||
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
|
||||
// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
|
||||
// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
|
||||
// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853)
|
||||
// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807)
|
||||
// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801)
|
||||
// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794)
|
||||
// 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762).
|
||||
// 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea().
|
||||
// 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures.
|
||||
// 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents.
|
||||
// 2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames.
|
||||
// 2024-05-15: Update for SDL3 api changes: SDLK_ renames.
|
||||
// 2024-04-15: Inputs: Re-enable calling SDL_StartTextInput()/SDL_StopTextInput() as SDL3 no longer enables it by default and should play nicer with IME.
|
||||
// 2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode().
|
||||
// 2023-11-13: Updated for recent SDL3 API changes.
|
||||
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
|
||||
// 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391)
|
||||
// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)
|
||||
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
|
||||
// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
|
||||
// 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog.
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_sdl3.h"
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#endif
|
||||
|
||||
// SDL
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdio.h> // for snprintf()
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
|
||||
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
|
||||
#else
|
||||
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
|
||||
#endif
|
||||
|
||||
// FIXME-LEGACY: remove when SDL 3.1.3 preview is released.
|
||||
#ifndef SDLK_APOSTROPHE
|
||||
#define SDLK_APOSTROPHE SDLK_QUOTE
|
||||
#endif
|
||||
#ifndef SDLK_GRAVE
|
||||
#define SDLK_GRAVE SDLK_BACKQUOTE
|
||||
#endif
|
||||
|
||||
// SDL Data
|
||||
struct ImGui_ImplSDL3_Data
|
||||
{
|
||||
SDL_Window* Window;
|
||||
SDL_WindowID WindowID;
|
||||
SDL_Renderer* Renderer;
|
||||
Uint64 Time;
|
||||
char* ClipboardTextData;
|
||||
char BackendPlatformName[48];
|
||||
|
||||
// IME handling
|
||||
SDL_Window* ImeWindow;
|
||||
|
||||
// Mouse handling
|
||||
Uint32 MouseWindowID;
|
||||
int MouseButtonsDown;
|
||||
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
|
||||
SDL_Cursor* MouseLastCursor;
|
||||
int MousePendingLeaveFrame;
|
||||
bool MouseCanUseGlobalState;
|
||||
bool MouseCanUseCapture;
|
||||
|
||||
// Gamepad handling
|
||||
ImVector<SDL_Gamepad*> Gamepads;
|
||||
ImGui_ImplSDL3_GamepadMode GamepadMode;
|
||||
bool WantUpdateGamepadsList;
|
||||
|
||||
ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
|
||||
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
|
||||
static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||
}
|
||||
|
||||
// Functions
|
||||
static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
if (bd->ClipboardTextData)
|
||||
SDL_free(bd->ClipboardTextData);
|
||||
bd->ClipboardTextData = SDL_GetClipboardText();
|
||||
return bd->ClipboardTextData;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text)
|
||||
{
|
||||
SDL_SetClipboardText(text);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
|
||||
SDL_Window* window = SDL_GetWindowFromID(window_id);
|
||||
if ((!(data->WantVisible || data->WantTextInput) || bd->ImeWindow != window) && bd->ImeWindow != nullptr)
|
||||
{
|
||||
SDL_StopTextInput(bd->ImeWindow);
|
||||
bd->ImeWindow = nullptr;
|
||||
}
|
||||
if (data->WantVisible)
|
||||
{
|
||||
SDL_Rect r;
|
||||
r.x = (int)data->InputPos.x;
|
||||
r.y = (int)data->InputPos.y;
|
||||
r.w = 1;
|
||||
r.h = (int)data->InputLineHeight;
|
||||
SDL_SetTextInputArea(window, &r, 0);
|
||||
bd->ImeWindow = window;
|
||||
}
|
||||
if (data->WantVisible || data->WantTextInput)
|
||||
SDL_StartTextInput(window);
|
||||
}
|
||||
|
||||
// Not static to allow third-party code to use that if they want to (but undocumented)
|
||||
ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
|
||||
ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
|
||||
{
|
||||
// Keypad doesn't have individual key values in SDL3
|
||||
switch (scancode)
|
||||
{
|
||||
case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0;
|
||||
case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1;
|
||||
case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2;
|
||||
case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3;
|
||||
case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4;
|
||||
case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5;
|
||||
case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6;
|
||||
case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7;
|
||||
case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8;
|
||||
case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9;
|
||||
case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal;
|
||||
case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide;
|
||||
case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
|
||||
case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract;
|
||||
case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd;
|
||||
case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter;
|
||||
case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual;
|
||||
default: break;
|
||||
}
|
||||
switch (keycode)
|
||||
{
|
||||
case SDLK_TAB: return ImGuiKey_Tab;
|
||||
case SDLK_LEFT: return ImGuiKey_LeftArrow;
|
||||
case SDLK_RIGHT: return ImGuiKey_RightArrow;
|
||||
case SDLK_UP: return ImGuiKey_UpArrow;
|
||||
case SDLK_DOWN: return ImGuiKey_DownArrow;
|
||||
case SDLK_PAGEUP: return ImGuiKey_PageUp;
|
||||
case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
|
||||
case SDLK_HOME: return ImGuiKey_Home;
|
||||
case SDLK_END: return ImGuiKey_End;
|
||||
case SDLK_INSERT: return ImGuiKey_Insert;
|
||||
case SDLK_DELETE: return ImGuiKey_Delete;
|
||||
case SDLK_BACKSPACE: return ImGuiKey_Backspace;
|
||||
case SDLK_SPACE: return ImGuiKey_Space;
|
||||
case SDLK_RETURN: return ImGuiKey_Enter;
|
||||
case SDLK_ESCAPE: return ImGuiKey_Escape;
|
||||
//case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe;
|
||||
case SDLK_COMMA: return ImGuiKey_Comma;
|
||||
//case SDLK_MINUS: return ImGuiKey_Minus;
|
||||
case SDLK_PERIOD: return ImGuiKey_Period;
|
||||
//case SDLK_SLASH: return ImGuiKey_Slash;
|
||||
case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
|
||||
//case SDLK_EQUALS: return ImGuiKey_Equal;
|
||||
//case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||
//case SDLK_BACKSLASH: return ImGuiKey_Backslash;
|
||||
//case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||
//case SDLK_GRAVE: return ImGuiKey_GraveAccent;
|
||||
case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
|
||||
case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
|
||||
case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
|
||||
case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
|
||||
case SDLK_PAUSE: return ImGuiKey_Pause;
|
||||
case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
|
||||
case SDLK_LSHIFT: return ImGuiKey_LeftShift;
|
||||
case SDLK_LALT: return ImGuiKey_LeftAlt;
|
||||
case SDLK_LGUI: return ImGuiKey_LeftSuper;
|
||||
case SDLK_RCTRL: return ImGuiKey_RightCtrl;
|
||||
case SDLK_RSHIFT: return ImGuiKey_RightShift;
|
||||
case SDLK_RALT: return ImGuiKey_RightAlt;
|
||||
case SDLK_RGUI: return ImGuiKey_RightSuper;
|
||||
case SDLK_APPLICATION: return ImGuiKey_Menu;
|
||||
case SDLK_0: return ImGuiKey_0;
|
||||
case SDLK_1: return ImGuiKey_1;
|
||||
case SDLK_2: return ImGuiKey_2;
|
||||
case SDLK_3: return ImGuiKey_3;
|
||||
case SDLK_4: return ImGuiKey_4;
|
||||
case SDLK_5: return ImGuiKey_5;
|
||||
case SDLK_6: return ImGuiKey_6;
|
||||
case SDLK_7: return ImGuiKey_7;
|
||||
case SDLK_8: return ImGuiKey_8;
|
||||
case SDLK_9: return ImGuiKey_9;
|
||||
case SDLK_A: return ImGuiKey_A;
|
||||
case SDLK_B: return ImGuiKey_B;
|
||||
case SDLK_C: return ImGuiKey_C;
|
||||
case SDLK_D: return ImGuiKey_D;
|
||||
case SDLK_E: return ImGuiKey_E;
|
||||
case SDLK_F: return ImGuiKey_F;
|
||||
case SDLK_G: return ImGuiKey_G;
|
||||
case SDLK_H: return ImGuiKey_H;
|
||||
case SDLK_I: return ImGuiKey_I;
|
||||
case SDLK_J: return ImGuiKey_J;
|
||||
case SDLK_K: return ImGuiKey_K;
|
||||
case SDLK_L: return ImGuiKey_L;
|
||||
case SDLK_M: return ImGuiKey_M;
|
||||
case SDLK_N: return ImGuiKey_N;
|
||||
case SDLK_O: return ImGuiKey_O;
|
||||
case SDLK_P: return ImGuiKey_P;
|
||||
case SDLK_Q: return ImGuiKey_Q;
|
||||
case SDLK_R: return ImGuiKey_R;
|
||||
case SDLK_S: return ImGuiKey_S;
|
||||
case SDLK_T: return ImGuiKey_T;
|
||||
case SDLK_U: return ImGuiKey_U;
|
||||
case SDLK_V: return ImGuiKey_V;
|
||||
case SDLK_W: return ImGuiKey_W;
|
||||
case SDLK_X: return ImGuiKey_X;
|
||||
case SDLK_Y: return ImGuiKey_Y;
|
||||
case SDLK_Z: return ImGuiKey_Z;
|
||||
case SDLK_F1: return ImGuiKey_F1;
|
||||
case SDLK_F2: return ImGuiKey_F2;
|
||||
case SDLK_F3: return ImGuiKey_F3;
|
||||
case SDLK_F4: return ImGuiKey_F4;
|
||||
case SDLK_F5: return ImGuiKey_F5;
|
||||
case SDLK_F6: return ImGuiKey_F6;
|
||||
case SDLK_F7: return ImGuiKey_F7;
|
||||
case SDLK_F8: return ImGuiKey_F8;
|
||||
case SDLK_F9: return ImGuiKey_F9;
|
||||
case SDLK_F10: return ImGuiKey_F10;
|
||||
case SDLK_F11: return ImGuiKey_F11;
|
||||
case SDLK_F12: return ImGuiKey_F12;
|
||||
case SDLK_F13: return ImGuiKey_F13;
|
||||
case SDLK_F14: return ImGuiKey_F14;
|
||||
case SDLK_F15: return ImGuiKey_F15;
|
||||
case SDLK_F16: return ImGuiKey_F16;
|
||||
case SDLK_F17: return ImGuiKey_F17;
|
||||
case SDLK_F18: return ImGuiKey_F18;
|
||||
case SDLK_F19: return ImGuiKey_F19;
|
||||
case SDLK_F20: return ImGuiKey_F20;
|
||||
case SDLK_F21: return ImGuiKey_F21;
|
||||
case SDLK_F22: return ImGuiKey_F22;
|
||||
case SDLK_F23: return ImGuiKey_F23;
|
||||
case SDLK_F24: return ImGuiKey_F24;
|
||||
case SDLK_AC_BACK: return ImGuiKey_AppBack;
|
||||
case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Fallback to scancode
|
||||
switch (scancode)
|
||||
{
|
||||
case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent;
|
||||
case SDL_SCANCODE_MINUS: return ImGuiKey_Minus;
|
||||
case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal;
|
||||
case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||
case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||
case SDL_SCANCODE_NONUSBACKSLASH: return ImGuiKey_Oem102;
|
||||
case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash;
|
||||
case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon;
|
||||
case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
|
||||
case SDL_SCANCODE_COMMA: return ImGuiKey_Comma;
|
||||
case SDL_SCANCODE_PERIOD: return ImGuiKey_Period;
|
||||
case SDL_SCANCODE_SLASH: return ImGuiKey_Slash;
|
||||
default: break;
|
||||
}
|
||||
return ImGuiKey_None;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
|
||||
}
|
||||
|
||||
|
||||
static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr;
|
||||
}
|
||||
|
||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
|
||||
bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr)
|
||||
return false;
|
||||
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
|
||||
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr)
|
||||
return false;
|
||||
//IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
|
||||
float wheel_x = -event->wheel.x;
|
||||
float wheel_y = event->wheel.y;
|
||||
io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||
io.AddMouseWheelEvent(wheel_x, wheel_y);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr)
|
||||
return false;
|
||||
int mouse_button = -1;
|
||||
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
|
||||
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
|
||||
if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
|
||||
if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
|
||||
if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
|
||||
if (mouse_button == -1)
|
||||
break;
|
||||
io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
|
||||
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN));
|
||||
bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr)
|
||||
return false;
|
||||
io.AddInputCharactersUTF8(event->text.text);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr)
|
||||
return false;
|
||||
ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod);
|
||||
//IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%s : key=%d ('%s'), scancode=%d ('%s'), mod=%X\n",
|
||||
// (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod);
|
||||
ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode);
|
||||
io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
|
||||
io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
|
||||
return false;
|
||||
bd->MouseWindowID = event->window.windowID;
|
||||
bd->MousePendingLeaveFrame = 0;
|
||||
return true;
|
||||
}
|
||||
// - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
|
||||
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
|
||||
// we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
|
||||
// FIXME: Unconfirmed whether this is still needed with SDL3.
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
|
||||
return false;
|
||||
bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1;
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
{
|
||||
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
|
||||
return false;
|
||||
io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
{
|
||||
bd->WantUpdateGamepadsList = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window)
|
||||
{
|
||||
viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window);
|
||||
viewport->PlatformHandleRaw = nullptr;
|
||||
#if defined(_WIN32) && !defined(__WINRT__)
|
||||
viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
|
||||
#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
|
||||
viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||
IM_UNUSED(sdl_gl_context); // Unused in this branch
|
||||
|
||||
const int ver_linked = SDL_GetVersion();
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
|
||||
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%u.%u.%u; %u.%u.%u)",
|
||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = bd->BackendPlatformName;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||
|
||||
bd->Window = window;
|
||||
bd->WindowID = SDL_GetWindowID(window);
|
||||
bd->Renderer = renderer;
|
||||
|
||||
// Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse()
|
||||
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
|
||||
bd->MouseCanUseGlobalState = false;
|
||||
bd->MouseCanUseCapture = false;
|
||||
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||
const char* sdl_backend = SDL_GetCurrentVideoDriver();
|
||||
const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
|
||||
for (const char* item : capture_and_global_state_whitelist)
|
||||
if (strncmp(sdl_backend, item, strlen(item)) == 0)
|
||||
bd->MouseCanUseGlobalState = bd->MouseCanUseCapture = true;
|
||||
#endif
|
||||
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
|
||||
platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
|
||||
platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData;
|
||||
platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url) == 0; };
|
||||
|
||||
// Gamepad handling
|
||||
bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
|
||||
bd->WantUpdateGamepadsList = true;
|
||||
|
||||
// Load mouse cursors
|
||||
bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT);
|
||||
bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE);
|
||||
bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER);
|
||||
bd->MouseCursors[ImGuiMouseCursor_Wait] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
|
||||
bd->MouseCursors[ImGuiMouseCursor_Progress] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_PROGRESS);
|
||||
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED);
|
||||
|
||||
// Set platform dependent data in viewport
|
||||
// Our mouse update function expect PlatformHandle to be filled for the main viewport
|
||||
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||
ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window);
|
||||
|
||||
// From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
|
||||
// Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
|
||||
// (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
|
||||
// It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
|
||||
// you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||
#endif
|
||||
|
||||
// From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
|
||||
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
|
||||
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
|
||||
{
|
||||
IM_UNUSED(sdl_gl_context); // Viewport branch will need this.
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
IM_ASSERT(0 && "Unsupported");
|
||||
#endif
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, renderer, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL3_InitForOther(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_CloseGamepads();
|
||||
|
||||
void ImGui_ImplSDL3_Shutdown()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
if (bd->ClipboardTextData)
|
||||
SDL_free(bd->ClipboardTextData);
|
||||
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||
SDL_DestroyCursor(bd->MouseCursors[cursor_n]);
|
||||
ImGui_ImplSDL3_CloseGamepads();
|
||||
|
||||
io.BackendPlatformName = nullptr;
|
||||
io.BackendPlatformUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateMouseData()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below)
|
||||
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
|
||||
// - SDL_CaptureMouse() let the OS know e.g. that our drags can extend outside of parent boundaries (we want updated position) and shouldn't trigger other operations outside.
|
||||
// - Debuggers under Linux tends to leave captured mouse on break, which may be very inconvenient, so to mitigate the issue we wait until mouse has moved to begin capture.
|
||||
if (bd->MouseCanUseCapture)
|
||||
{
|
||||
bool want_capture = false;
|
||||
for (int button_n = 0; button_n < ImGuiMouseButton_COUNT && !want_capture; button_n++)
|
||||
if (ImGui::IsMouseDragging(button_n, 1.0f))
|
||||
want_capture = true;
|
||||
SDL_CaptureMouse(want_capture);
|
||||
}
|
||||
|
||||
SDL_Window* focused_window = SDL_GetKeyboardFocus();
|
||||
const bool is_app_focused = (bd->Window == focused_window);
|
||||
#else
|
||||
SDL_Window* focused_window = bd->Window;
|
||||
const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only
|
||||
#endif
|
||||
if (is_app_focused)
|
||||
{
|
||||
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
|
||||
if (io.WantSetMousePos)
|
||||
SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y);
|
||||
|
||||
// (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured)
|
||||
const bool is_relative_mouse_mode = SDL_GetWindowRelativeMouseMode(bd->Window);
|
||||
if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode)
|
||||
{
|
||||
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
|
||||
float mouse_x_global, mouse_y_global;
|
||||
int window_x, window_y;
|
||||
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
|
||||
SDL_GetWindowPosition(focused_window, &window_x, &window_y);
|
||||
io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateMouseCursor()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
return;
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
SDL_HideCursor();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
|
||||
if (bd->MouseLastCursor != expected_cursor)
|
||||
{
|
||||
SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
|
||||
bd->MouseLastCursor = expected_cursor;
|
||||
}
|
||||
SDL_ShowCursor();
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_CloseGamepads()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
|
||||
for (SDL_Gamepad* gamepad : bd->Gamepads)
|
||||
SDL_CloseGamepad(gamepad);
|
||||
bd->Gamepads.resize(0);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count)
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
ImGui_ImplSDL3_CloseGamepads();
|
||||
if (mode == ImGui_ImplSDL3_GamepadMode_Manual)
|
||||
{
|
||||
IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0);
|
||||
for (int n = 0; n < manual_gamepads_count; n++)
|
||||
bd->Gamepads.push_back(manual_gamepads_array[n]);
|
||||
}
|
||||
else
|
||||
{
|
||||
IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
|
||||
bd->WantUpdateGamepadsList = true;
|
||||
}
|
||||
bd->GamepadMode = mode;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no)
|
||||
{
|
||||
bool merged_value = false;
|
||||
for (SDL_Gamepad* gamepad : bd->Gamepads)
|
||||
merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0;
|
||||
io.AddKeyEvent(key, merged_value);
|
||||
}
|
||||
|
||||
static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; }
|
||||
static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1)
|
||||
{
|
||||
float merged_value = 0.0f;
|
||||
for (SDL_Gamepad* gamepad : bd->Gamepads)
|
||||
{
|
||||
float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
|
||||
if (merged_value < vn)
|
||||
merged_value = vn;
|
||||
}
|
||||
io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_UpdateGamepads()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
|
||||
// Update list of gamepads to use
|
||||
if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
|
||||
{
|
||||
ImGui_ImplSDL3_CloseGamepads();
|
||||
int sdl_gamepads_count = 0;
|
||||
SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count);
|
||||
for (int n = 0; n < sdl_gamepads_count; n++)
|
||||
if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n]))
|
||||
{
|
||||
bd->Gamepads.push_back(gamepad);
|
||||
if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst)
|
||||
break;
|
||||
}
|
||||
bd->WantUpdateGamepadsList = false;
|
||||
SDL_free(sdl_gamepads);
|
||||
}
|
||||
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
if (bd->Gamepads.Size == 0)
|
||||
return;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
|
||||
// Update gamepad inputs
|
||||
const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value.
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK);
|
||||
ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX, +thumb_dead_zone, +32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY, +thumb_dead_zone, +32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768);
|
||||
ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(SDL_Window* window, ImVec2* out_size, ImVec2* out_framebuffer_scale)
|
||||
{
|
||||
int w, h;
|
||||
int display_w, display_h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
|
||||
w = h = 0;
|
||||
SDL_GetWindowSizeInPixels(window, &display_w, &display_h);
|
||||
if (out_size != nullptr)
|
||||
*out_size = ImVec2((float)w, (float)h);
|
||||
if (out_framebuffer_scale != nullptr)
|
||||
*out_framebuffer_scale = (w > 0 && h > 0) ? ImVec2((float)display_w / w, (float)display_h / h) : ImVec2(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL3_NewFrame()
|
||||
{
|
||||
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Setup main viewport size (every frame to accommodate for window resizing)
|
||||
ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale);
|
||||
|
||||
// Setup time step (we could also use SDL_GetTicksNS() available since SDL3)
|
||||
// (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
|
||||
static Uint64 frequency = SDL_GetPerformanceFrequency();
|
||||
Uint64 current_time = SDL_GetPerformanceCounter();
|
||||
if (current_time <= bd->Time)
|
||||
current_time = bd->Time + 1;
|
||||
io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
|
||||
bd->Time = current_time;
|
||||
|
||||
if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
|
||||
{
|
||||
bd->MouseWindowID = 0;
|
||||
bd->MousePendingLeaveFrame = 0;
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||
}
|
||||
|
||||
ImGui_ImplSDL3_UpdateMouseData();
|
||||
ImGui_ImplSDL3_UpdateMouseCursor();
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
ImGui_ImplSDL3_UpdateGamepads();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
@@ -1,314 +0,0 @@
|
||||
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
|
||||
// (Requires: SDL 3.1.8+)
|
||||
|
||||
// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete.
|
||||
// For a multi-platform app consider using other technologies:
|
||||
// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API.
|
||||
// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers.
|
||||
// If your application wants to render any non trivial amount of graphics other than UI,
|
||||
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user
|
||||
// and it might be difficult to step out of those boundaries.
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// CHANGELOG
|
||||
// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer3_CreateFontsTexture() and ImGui_ImplSDLRenderer3_DestroyFontsTexture().
|
||||
// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color.
|
||||
// 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
|
||||
// 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009).
|
||||
// 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
|
||||
// 2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly.
|
||||
// 2023-05-30: Initial version.
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_sdlrenderer3.h"
|
||||
#include <stdint.h> // intptr_t
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
// SDL
|
||||
#include <SDL3/SDL.h>
|
||||
#if !SDL_VERSION_ATLEAST(3,0,0)
|
||||
#error This backend requires SDL 3.0.0+
|
||||
#endif
|
||||
|
||||
// SDL_Renderer data
|
||||
struct ImGui_ImplSDLRenderer3_Data
|
||||
{
|
||||
SDL_Renderer* Renderer; // Main viewport's renderer
|
||||
ImVector<SDL_FColor> ColorBuffer;
|
||||
|
||||
ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||
}
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||
IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)();
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_sdlrenderer3";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
|
||||
bd->Renderer = renderer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_Shutdown()
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
|
||||
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer)
|
||||
{
|
||||
// Clear out any viewports and cliprect set by the user
|
||||
// FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
|
||||
SDL_SetRenderViewport(renderer, nullptr);
|
||||
SDL_SetRenderClipRect(renderer, nullptr);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_NewFrame()
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?");
|
||||
IM_UNUSED(bd);
|
||||
}
|
||||
|
||||
// https://github.com/libsdl-org/SDL/issues/9009
|
||||
static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVector<SDL_FColor>& colors_out, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_Color* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices)
|
||||
{
|
||||
const Uint8* color2 = (const Uint8*)color;
|
||||
colors_out.resize(num_vertices);
|
||||
SDL_FColor* color3 = colors_out.Data;
|
||||
for (int i = 0; i < num_vertices; i++)
|
||||
{
|
||||
color3[i].r = color->r / 255.0f;
|
||||
color3[i].g = color->g / 255.0f;
|
||||
color3[i].b = color->b / 255.0f;
|
||||
color3[i].a = color->a / 255.0f;
|
||||
color2 += color_stride;
|
||||
color = (const SDL_Color*)color2;
|
||||
}
|
||||
return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color3, sizeof(*color3), uv, uv_stride, num_vertices, indices, num_indices, size_indices);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer)
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
|
||||
// If there's a scale factor set by the user, use that instead
|
||||
// If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
|
||||
// to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
|
||||
float rsx = 1.0f;
|
||||
float rsy = 1.0f;
|
||||
SDL_GetRenderScale(renderer, &rsx, &rsy);
|
||||
ImVec2 render_scale;
|
||||
render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f;
|
||||
render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f;
|
||||
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y);
|
||||
if (fb_width == 0 || fb_height == 0)
|
||||
return;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplSDLRenderer3_UpdateTexture(tex);
|
||||
|
||||
// Backup SDL_Renderer state that will be modified to restore it afterwards
|
||||
struct BackupSDLRendererState
|
||||
{
|
||||
SDL_Rect Viewport;
|
||||
bool ViewportEnabled;
|
||||
bool ClipEnabled;
|
||||
SDL_Rect ClipRect;
|
||||
};
|
||||
BackupSDLRendererState old = {};
|
||||
old.ViewportEnabled = SDL_RenderViewportSet(renderer);
|
||||
old.ClipEnabled = SDL_RenderClipEnabled(renderer);
|
||||
SDL_GetRenderViewport(renderer, &old.Viewport);
|
||||
SDL_GetRenderClipRect(renderer, &old.ClipRect);
|
||||
|
||||
// Setup desired state
|
||||
ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
|
||||
|
||||
// Setup render state structure (for callbacks and custom texture bindings)
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
ImGui_ImplSDLRenderer3_RenderState render_state;
|
||||
render_state.Renderer = renderer;
|
||||
platform_io.Renderer_RenderState = &render_state;
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
ImVec2 clip_scale = render_scale;
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* draw_list = draw_data->CmdLists[n];
|
||||
const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data;
|
||||
const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data;
|
||||
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
|
||||
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
|
||||
if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
|
||||
if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) };
|
||||
SDL_SetRenderClipRect(renderer, &r);
|
||||
|
||||
const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos));
|
||||
const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv));
|
||||
const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+
|
||||
|
||||
// Bind texture, Draw
|
||||
SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID();
|
||||
SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex,
|
||||
xy, (int)sizeof(ImDrawVert),
|
||||
color, (int)sizeof(ImDrawVert),
|
||||
uv, (int)sizeof(ImDrawVert),
|
||||
draw_list->VtxBuffer.Size - pcmd->VtxOffset,
|
||||
idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
|
||||
// Restore modified SDL_Renderer state
|
||||
SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr);
|
||||
SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex)
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
|
||||
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
|
||||
// Create texture
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height);
|
||||
IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!");
|
||||
SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch());
|
||||
SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_LINEAR);
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)sdl_texture);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantUpdates)
|
||||
{
|
||||
// Update selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
||||
SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID;
|
||||
for (ImTextureRect& r : tex->Updates)
|
||||
{
|
||||
SDL_Rect sdl_r = { r.x, r.y, r.w, r.h };
|
||||
SDL_UpdateTexture(sdl_texture, &sdl_r, tex->GetPixelsAt(r.x, r.y), tex->GetPitch());
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantDestroy)
|
||||
{
|
||||
SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID;
|
||||
if (sdl_texture == nullptr)
|
||||
return;
|
||||
SDL_DestroyTexture(sdl_texture);
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_CreateDeviceObjects()
|
||||
{
|
||||
}
|
||||
|
||||
void ImGui_ImplSDLRenderer3_DestroyDeviceObjects()
|
||||
{
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
{
|
||||
tex->SetStatus(ImTextureStatus_WantDestroy);
|
||||
ImGui_ImplSDLRenderer3_UpdateTexture(tex);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,141 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||
//-----------------------------------------------------------------------------
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
//#define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||
//#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
||||
// dear imgui: Platform Backend for SDL3
|
||||
// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Gamepad support.
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: IME support.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
struct SDL_Gamepad;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_InitForOther(SDL_Window* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event);
|
||||
|
||||
// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this.
|
||||
// When using manual mode, caller is responsible for opening/closing gamepad.
|
||||
enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual };
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
@@ -1,53 +0,0 @@
|
||||
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
|
||||
// (Requires: SDL 3.1.8+)
|
||||
|
||||
// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete.
|
||||
// For a multi-platform app consider using other technologies:
|
||||
// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API.
|
||||
// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers.
|
||||
// If your application wants to render any non trivial amount of graphics other than UI,
|
||||
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user
|
||||
// and it might be difficult to step out of those boundaries.
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct SDL_Renderer;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer);
|
||||
|
||||
// Called by Init/NewFrame/Shutdown
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
|
||||
|
||||
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
|
||||
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
// [BETA] Selected render state data shared with callbacks.
|
||||
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLRenderer3_RenderDrawData() call.
|
||||
// (Please open an issue if you feel you need access to more data)
|
||||
struct ImGui_ImplSDLRenderer3_RenderState
|
||||
{
|
||||
SDL_Renderer* Renderer;
|
||||
};
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,627 +0,0 @@
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
//
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ idf_component_register(SRCS
|
||||
src/common/ScrollBar.cpp
|
||||
src/common/Widget.cpp
|
||||
src/data/MenuItem.cpp
|
||||
src/ui/ExternalDevices.cpp
|
||||
src/ui/LightMenu.cpp
|
||||
src/ui/MainMenu.cpp
|
||||
src/ui/ClockScreenSaver.cpp
|
||||
@@ -13,6 +14,7 @@ idf_component_register(SRCS
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
u8g2
|
||||
connectivity-manager
|
||||
led-manager
|
||||
persistence-manager
|
||||
simulator
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Menu.h"
|
||||
|
||||
class ExternalDevices final : public Menu
|
||||
{
|
||||
public:
|
||||
explicit ExternalDevices(menu_options_t *options);
|
||||
|
||||
private:
|
||||
void onButtonPressed(const MenuItem &menuItem, ButtonType button) override;
|
||||
menu_options_t *m_options;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "ui/ExternalDevices.h"
|
||||
|
||||
#include "ble/ble_device.h"
|
||||
#include "ble/ble_scanner.h"
|
||||
#include "ble_manager.h"
|
||||
|
||||
ExternalDevices::ExternalDevices(menu_options_t *options) : Menu(options), m_options(options)
|
||||
{
|
||||
for (int i = 0; i < get_device_count(); i++)
|
||||
{
|
||||
device_info_t *device = get_device(i);
|
||||
if (device != nullptr)
|
||||
{
|
||||
addText(i, device->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalDevices::onButtonPressed(const MenuItem &menuItem, const ButtonType button)
|
||||
{
|
||||
if (button == ButtonType::SELECT)
|
||||
{
|
||||
ble_connect_to_device(menuItem.getId());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ui/MainMenu.h"
|
||||
|
||||
#include "common/Widget.h"
|
||||
#include "ui/ExternalDevices.h"
|
||||
#include "ui/LightMenu.h"
|
||||
#include "ui/SettingsMenu.h"
|
||||
|
||||
@@ -33,6 +34,10 @@ void MainMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType button
|
||||
widget = std::make_shared<SettingsMenu>(m_options);
|
||||
break;
|
||||
|
||||
case MainMenuItem::EXTERNAL_DEVICES:
|
||||
widget = std::make_shared<ExternalDevices>(m_options);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ menu "Led Manager Configuration"
|
||||
|
||||
config LED_STRIP_MAX_LEDS
|
||||
int "Maximum number of LEDs"
|
||||
default 500
|
||||
default 800
|
||||
help
|
||||
The maximum number of LEDs that can be controlled.
|
||||
endmenu
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "color.h"
|
||||
#include "esp_check.h"
|
||||
#include <esp_check.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Number of LEDs to be controlled
|
||||
@@ -18,10 +18,11 @@ typedef enum
|
||||
// This is the structure you pass from the outside to define a behavior
|
||||
typedef struct
|
||||
{
|
||||
led_mode_t mode;
|
||||
rgb_t color;
|
||||
uint32_t on_time_ms; // Only relevant for BLINK
|
||||
uint32_t off_time_ms; // Only relevant for BLINK
|
||||
rgb_t color;
|
||||
uint8_t index;
|
||||
led_mode_t mode;
|
||||
} led_behavior_t;
|
||||
|
||||
__BEGIN_DECLS
|
||||
@@ -42,5 +43,5 @@ esp_err_t led_status_init(int gpio_num);
|
||||
* @param behavior The structure with the desired behavior.
|
||||
* @return esp_err_t ESP_OK on success, ESP_ERR_INVALID_ARG on invalid index.
|
||||
*/
|
||||
esp_err_t led_status_set_behavior(uint8_t index, led_behavior_t behavior);
|
||||
esp_err_t led_status_set_behavior(led_behavior_t behavior);
|
||||
__END_DECLS
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "led_status.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h" // For high-resolution timestamps
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "led_strip.h"
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h> // For high-resolution timestamps
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/task.h>
|
||||
#include <led_strip.h>
|
||||
|
||||
static const char *TAG = "led_status";
|
||||
|
||||
@@ -32,7 +32,7 @@ static void led_status_task(void *pvParameters)
|
||||
uint64_t now_us = esp_timer_get_time();
|
||||
|
||||
// Take the mutex to safely access the control data
|
||||
if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
|
||||
if (xSemaphoreTake(mutex, pdMS_TO_TICKS(100)) == pdTRUE)
|
||||
{
|
||||
|
||||
for (int i = 0; i < STATUS_LED_COUNT; i++)
|
||||
@@ -75,6 +75,10 @@ static void led_status_task(void *pvParameters)
|
||||
// Release the mutex
|
||||
xSemaphoreGive(mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "Failed to acquire mutex within timeout");
|
||||
}
|
||||
|
||||
// Update the physical LED strip with the new values
|
||||
led_strip_refresh(led_strip);
|
||||
@@ -119,27 +123,33 @@ esp_err_t led_status_init(int gpio_num)
|
||||
}
|
||||
|
||||
// Start task
|
||||
xTaskCreate(led_status_task, "led_status_task", 2048, NULL, tskIDLE_PRIORITY + 1, NULL);
|
||||
xTaskCreatePinnedToCore(led_status_task, "led_status_task", 2048, NULL, tskIDLE_PRIORITY + 2, NULL,
|
||||
CONFIG_FREERTOS_NUMBER_OF_CORES - 1);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Function to set the behavior
|
||||
esp_err_t led_status_set_behavior(uint8_t index, led_behavior_t behavior)
|
||||
esp_err_t led_status_set_behavior(led_behavior_t behavior)
|
||||
{
|
||||
if (index >= STATUS_LED_COUNT)
|
||||
if (behavior.index >= STATUS_LED_COUNT)
|
||||
{
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
|
||||
if (xSemaphoreTake(mutex, pdMS_TO_TICKS(100)) == pdTRUE)
|
||||
{
|
||||
led_controls[index].behavior = behavior;
|
||||
led_controls[behavior.index].behavior = behavior;
|
||||
// Reset internal state variables to start the new pattern cleanly
|
||||
led_controls[index].is_on_in_blink = false;
|
||||
led_controls[index].last_toggle_time_us = esp_timer_get_time();
|
||||
led_controls[behavior.index].is_on_in_blink = false;
|
||||
led_controls[behavior.index].last_toggle_time_us = esp_timer_get_time();
|
||||
xSemaphoreGive(mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to acquire mutex for set_behavior");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -29,9 +29,12 @@ static void set_all_pixels(const rgb_t color)
|
||||
}
|
||||
led_strip_refresh(led_strip);
|
||||
|
||||
led_behavior_t led2_behavior = {.mode = LED_MODE_SOLID,
|
||||
.color = {.red = color.red, .green = color.green, .blue = color.blue}};
|
||||
led_status_set_behavior(2, led2_behavior);
|
||||
led_behavior_t led_behavior = {
|
||||
.index = 2,
|
||||
.mode = LED_MODE_SOLID,
|
||||
.color = {.red = color.red, .green = color.green, .blue = color.blue},
|
||||
};
|
||||
led_status_set_behavior(led_behavior);
|
||||
}
|
||||
|
||||
void led_strip_task(void *pvParameters)
|
||||
@@ -90,7 +93,8 @@ esp_err_t led_strip_init(void)
|
||||
|
||||
set_all_pixels((rgb_t){.red = 0, .green = 0, .blue = 0});
|
||||
|
||||
xTaskCreate(led_strip_task, "led_strip_task", 4096, NULL, tskIDLE_PRIORITY + 1, NULL);
|
||||
xTaskCreatePinnedToCore(led_strip_task, "led_strip_task", 4096, NULL, tskIDLE_PRIORITY + 1, NULL,
|
||||
CONFIG_FREERTOS_NUMBER_OF_CORES - 1);
|
||||
|
||||
ESP_LOGI(TAG, "LED strip initialized");
|
||||
|
||||
|
||||
@@ -345,8 +345,8 @@ void start_simulation_task(void)
|
||||
|
||||
config->cycle_duration_minutes = 15;
|
||||
|
||||
if (xTaskCreate(simulate_cycle, "simulate_cycle", 4096, (void *)config, tskIDLE_PRIORITY + 1,
|
||||
&simulation_task_handle) != pdPASS)
|
||||
if (xTaskCreatePinnedToCore(simulate_cycle, "simulate_cycle", 4096, (void *)config, tskIDLE_PRIORITY + 1,
|
||||
&simulation_task_handle, CONFIG_FREERTOS_NUMBER_OF_CORES - 1) != pdPASS)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to create simulation task.");
|
||||
heap_caps_free(config);
|
||||
|
||||
@@ -164,15 +164,16 @@ static void handle_button(uint8_t button)
|
||||
|
||||
void app_task(void *args)
|
||||
{
|
||||
esp_task_wdt_add(NULL);
|
||||
|
||||
if (i2c_bus_scan_and_check() != ESP_OK)
|
||||
{
|
||||
led_behavior_t led0_behavior = {.mode = LED_MODE_BLINK,
|
||||
.color = {.red = 50, .green = 0, .blue = 0},
|
||||
.on_time_ms = 1000,
|
||||
.off_time_ms = 500};
|
||||
led_status_set_behavior(0, led0_behavior);
|
||||
led_behavior_t led_behavior = {
|
||||
.on_time_ms = 1000,
|
||||
.off_time_ms = 500,
|
||||
.color = {.red = 50, .green = 0, .blue = 0},
|
||||
.index = 0,
|
||||
.mode = LED_MODE_BLINK,
|
||||
};
|
||||
led_status_set_behavior(led_behavior);
|
||||
|
||||
ESP_LOGE(TAG, "Display not found on I2C bus");
|
||||
vTaskDelete(nullptr);
|
||||
@@ -194,8 +195,6 @@ void app_task(void *args)
|
||||
|
||||
while (true)
|
||||
{
|
||||
esp_task_wdt_reset();
|
||||
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
|
||||
if (m_widget != nullptr)
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
#include "button_gpio.h"
|
||||
#include "common.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_insights.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "iot_button.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_insights.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_mac.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <iot_button.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
@@ -31,8 +31,9 @@ void app_main(void)
|
||||
|
||||
led_strip_init();
|
||||
|
||||
xTaskCreatePinnedToCore(app_task, "app_task", 8192, NULL, tskIDLE_PRIORITY + 1, NULL, portNUM_PROCESSORS - 1);
|
||||
// xTaskCreatePinnedToCore(ble_manager_task, "ble_manager", 4096, NULL, tskIDLE_PRIORITY + 1, NULL,
|
||||
// portNUM_PROCESSORS - 1);
|
||||
xTaskCreatePinnedToCore(app_task, "app_task", 8192, NULL, tskIDLE_PRIORITY + 5, NULL,
|
||||
CONFIG_FREERTOS_NUMBER_OF_CORES - 1);
|
||||
xTaskCreatePinnedToCore(ble_manager_task, "ble_manager", 4096, NULL, tskIDLE_PRIORITY + 1, NULL,
|
||||
CONFIG_FREERTOS_NUMBER_OF_CORES - 1);
|
||||
}
|
||||
__END_DECLS
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# Name , Type , SubType , Offset , Size , Flags
|
||||
nvs , data , nvs , 0x9000 , 24k ,
|
||||
nvs , data , nvs , 0x9000 , 16k ,
|
||||
otadata , data , ota , , 8k ,
|
||||
phy_init , data , phy , , 4k ,
|
||||
factory , app , factory , 0x10000 , 2048K ,
|
||||
storage , data , spiffs , , 1024K ,
|
||||
ota_0 , app , ota_0 , 0x10000 , 1536K ,
|
||||
ota_1 , app , ota_1 , , 1536K ,
|
||||
storage , data , spiffs , , 256K ,
|
||||
coredump , data , coredump , , 576k ,
|
||||
fctry , data , nvs , , 24k ,
|
||||
|
||||
|
@@ -1,26 +0,0 @@
|
||||
#include "Common.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
auto CreateWindow(const char *title, const int width, const int height) -> Window *
|
||||
{
|
||||
|
||||
constexpr uint32_t window_flag = SDL_WINDOW_HIDDEN;
|
||||
const auto window = SDL_CreateWindow(title, width, height, window_flag);
|
||||
if (window == nullptr)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window", SDL_GetError(), nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR);
|
||||
if (const auto renderer = SDL_CreateRendererWithProperties(props); renderer == nullptr)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create renderer", SDL_GetError(), nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new Window(window);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* @file Common.h
|
||||
* @brief Common utility functions and window management for application framework
|
||||
* @details This header defines common utility functions that are shared across
|
||||
* the application framework. It provides essential functionality for
|
||||
* window creation and management, serving as a bridge between the
|
||||
* application layer and the underlying windowing system.
|
||||
* @author System Control Team
|
||||
* @date 2025-06-20
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "model/Window.h"
|
||||
|
||||
/**
|
||||
* @brief Creates a new window with specified title and dimensions
|
||||
* @param title Null-terminated string containing the window title text
|
||||
* @param width Window width in pixels
|
||||
* @param height Window height in pixels
|
||||
* @return Pointer to the newly created Window object, or nullptr on failure
|
||||
*
|
||||
* @pre title must not be nullptr and should contain valid display text
|
||||
* @pre width and height must be positive values within system display limits
|
||||
* @post A new Window object is allocated and initialized with the specified parameters
|
||||
* @post The returned Window pointer is ready for use with window management functions
|
||||
*
|
||||
* @details This function creates a new Window instance with the specified
|
||||
* title and dimensions. It handles the underlying window system
|
||||
* initialization, memory allocation, and setup required to create
|
||||
* a functional window object.
|
||||
*
|
||||
* The window creation process includes:
|
||||
* - Memory allocation for the Window structure
|
||||
* - Initialization of window properties (title, dimensions, state)
|
||||
* - Registration with the window management system
|
||||
* - Setup of default window behavior and event handling
|
||||
*
|
||||
* The returned window pointer can be used with other window management
|
||||
* functions to display content, handle events, and manage the window
|
||||
* lifecycle. The caller is responsible for properly managing the window
|
||||
* lifetime and ensuring proper cleanup when the window is no longer needed.
|
||||
*
|
||||
* @note The returned pointer must be properly managed by the caller
|
||||
* @note Window resources should be freed when no longer needed
|
||||
* @note The title string is copied internally and can be safely modified
|
||||
* or freed after this function returns
|
||||
*
|
||||
* @see Window class for window object interface and methods
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* auto* window = CreateWindow("System Control", 800, 600);
|
||||
* if (window != nullptr) {
|
||||
* // Use the window...
|
||||
* // Clean up when done
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
auto CreateWindow(const char *title, int width, int height) -> Window *;
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "string"
|
||||
|
||||
const std::string MyProject = "system_control";
|
||||
const std::string MyProjectVersion = "0.0.1";
|
||||
constexpr uint8_t MyProjectVersionMajor = 0;
|
||||
constexpr uint8_t MyProjectVersionMinor = 0;
|
||||
constexpr uint8_t MyProjectVersionPatch = 1;
|
||||
const std::string MyProjectBuildDate = "";
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "string"
|
||||
|
||||
const std::string MyProject = "@PROJECT_NAME@";
|
||||
const std::string MyProjectVersion = "@PROJECT_VERSION@";
|
||||
constexpr uint8_t MyProjectVersionMajor = @PROJECT_VERSION_MAJOR@;
|
||||
constexpr uint8_t MyProjectVersionMinor = @PROJECT_VERSION_MINOR@;
|
||||
constexpr uint8_t MyProjectVersionPatch = @PROJECT_VERSION_PATCH@;
|
||||
const std::string MyProjectBuildDate = "@PROJECT_BUILD_DATE@";
|
||||
@@ -1,110 +0,0 @@
|
||||
#include "debug/debug_overlay.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "Matrix.h"
|
||||
#include "Version.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include <imgui_impl_sdlrenderer3.h>
|
||||
|
||||
namespace DebugOverlay
|
||||
{
|
||||
void Init(const AppContext *context)
|
||||
{
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io{ImGui::GetIO()};
|
||||
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(context->MainWindow(), context->MainRenderer());
|
||||
ImGui_ImplSDLRenderer3_Init(context->MainRenderer());
|
||||
}
|
||||
|
||||
void Update(AppContext *context, const SDL_Event *event)
|
||||
{
|
||||
ImGui_ImplSDL3_ProcessEvent(event);
|
||||
|
||||
if (show_led_matrix)
|
||||
{
|
||||
if (!context->LedMatrixRenderer())
|
||||
{
|
||||
const auto win = CreateWindow("LED Matrix", width * 50, height * 50);
|
||||
SDL_SetWindowFocusable(win->window(), false);
|
||||
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
|
||||
SDL_SetWindowPosition(win->window(), 0, 0);
|
||||
SDL_ShowWindow(win->window());
|
||||
|
||||
const auto windowId = SDL_GetWindowID(win->window());
|
||||
context->SetMatrix(new Matrix(windowId, win->renderer(), width, height));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context->LedMatrixRenderer())
|
||||
{
|
||||
int window_count = 0;
|
||||
if (SDL_Window **windows = SDL_GetWindows(&window_count))
|
||||
{
|
||||
for (int i = 0; i < window_count; ++i)
|
||||
{
|
||||
if (SDL_Window *window = windows[i]; context->LedMatrixId() == SDL_GetWindowID(window))
|
||||
{
|
||||
SDL_DestroyRenderer(context->LedMatrixRenderer());
|
||||
SDL_DestroyWindow(window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_free(windows);
|
||||
}
|
||||
|
||||
SDL_DestroyRenderer(context->LedMatrixRenderer());
|
||||
|
||||
context->SetMatrix(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Render(const AppContext *context)
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (show_debug_window && ImGui::BeginMainMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("Config"))
|
||||
{
|
||||
ImGui::Checkbox("Show LED Matrix", &show_led_matrix);
|
||||
ImGui::Checkbox("Show Unhandled Events", &show_unhandled_events);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Help"))
|
||||
{
|
||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||
ImGui::SeparatorText("App Info");
|
||||
ImGui::Text("Project: %s", MyProject.c_str());
|
||||
ImGui::Text("Version: %s", MyProjectVersion.c_str());
|
||||
ImGui::Text("Build Date: %s", MyProjectBuildDate.c_str());
|
||||
ImGui::Text("ImGui Version: %s", ImGui::GetVersion());
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), context->MainRenderer());
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
} // namespace DebugOverlay
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "model/AppContext.h"
|
||||
|
||||
namespace DebugOverlay
|
||||
{
|
||||
inline bool show_debug_window = false;
|
||||
inline bool show_unhandled_events = false;
|
||||
inline bool show_led_matrix = false;
|
||||
|
||||
constexpr auto width = 8;
|
||||
constexpr auto height = 8;
|
||||
|
||||
void Init(const AppContext *context);
|
||||
|
||||
void Update(AppContext *context, const SDL_Event *event);
|
||||
|
||||
void Render(const AppContext *context);
|
||||
|
||||
void Cleanup();
|
||||
} // namespace DebugOverlay
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "u8g2.h"
|
||||
|
||||
#define U8G2_SCREEN_WIDTH (128)
|
||||
#define U8G2_SCREEN_HEIGHT (64)
|
||||
#define U8G2_SCREEN_FACTOR (3)
|
||||
#define U8G2_SCREEN_PADDING (25)
|
||||
|
||||
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
|
||||
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "hal/u8g2_hal_sdl.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <cstdint>
|
||||
|
||||
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case U8X8_MSG_BYTE_SEND:
|
||||
case U8X8_MSG_BYTE_INIT:
|
||||
case U8X8_MSG_BYTE_SET_DC:
|
||||
case U8X8_MSG_BYTE_START_TRANSFER:
|
||||
case U8X8_MSG_BYTE_END_TRANSFER:
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case U8X8_MSG_DELAY_MILLI:
|
||||
SDL_Delay(arg_int);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_DELAY_10MICRO:
|
||||
SDL_Delay((arg_int + 99) / 100);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_DELAY_100NANO:
|
||||
case U8X8_MSG_DELAY_NANO:
|
||||
SDL_Delay(1);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_GPIO_AND_DELAY_INIT:
|
||||
case U8X8_MSG_GPIO_RESET:
|
||||
case U8X8_MSG_GPIO_CS:
|
||||
case U8X8_MSG_GPIO_DC:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
#include <vector>
|
||||
|
||||
#include <u8g2.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "common/Common.h"
|
||||
#include "debug/debug_overlay.h"
|
||||
#include "hal/u8g2_hal_sdl.h"
|
||||
#include "model/AppContext.h"
|
||||
#include "ui/Device.h"
|
||||
#include "ui/UIWidget.h"
|
||||
#include "ui/widgets/Button.h"
|
||||
#include "ui/widgets/D_Pad.h"
|
||||
|
||||
constexpr unsigned int WINDOW_WIDTH = (U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING +
|
||||
2 * BUTTON_WIDTH + DPAD_WIDTH + 2 * U8G2_SCREEN_PADDING);
|
||||
constexpr unsigned int WINDOW_HEIGHT = (U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + 50);
|
||||
|
||||
std::shared_ptr<Device> device;
|
||||
std::vector<std::shared_ptr<UIWidget>> widgets;
|
||||
|
||||
static uint64_t lastFrameTime = 0;
|
||||
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) == false)
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL! -> %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (TTF_Init() == false)
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize TTF! -> %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
const auto win = CreateWindow("System Control (Simulator)", WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
if (!win)
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window! -> %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
|
||||
SDL_SetWindowPosition(win->window(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_ShowWindow(win->window());
|
||||
|
||||
const auto context = new AppContext(win);
|
||||
*appstate = context;
|
||||
|
||||
device = std::make_shared<Device>(context);
|
||||
widgets.push_back(device);
|
||||
|
||||
DebugOverlay::Init(context);
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
const auto context = static_cast<AppContext *>(appstate);
|
||||
DebugOverlay::Update(context, event);
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
/// multi window
|
||||
if (SDL_GetWindowID(context->MainWindow()) == event->window.windowID)
|
||||
{
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_QUIT:
|
||||
/// single window application
|
||||
return SDL_APP_SUCCESS;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
switch (event->key.key)
|
||||
{
|
||||
case SDLK_ESCAPE:
|
||||
return SDL_APP_SUCCESS;
|
||||
|
||||
case SDLK_UP:
|
||||
device->OnButtonClicked(ButtonType::UP);
|
||||
break;
|
||||
|
||||
case SDLK_DOWN:
|
||||
device->OnButtonClicked(ButtonType::DOWN);
|
||||
break;
|
||||
|
||||
case SDLK_LEFT:
|
||||
device->OnButtonClicked(ButtonType::LEFT);
|
||||
break;
|
||||
|
||||
case SDLK_RIGHT:
|
||||
device->OnButtonClicked(ButtonType::RIGHT);
|
||||
break;
|
||||
|
||||
case SDLK_RETURN:
|
||||
device->OnButtonClicked(ButtonType::SELECT);
|
||||
break;
|
||||
|
||||
case SDLK_BACKSPACE:
|
||||
device->OnButtonClicked(ButtonType::BACK);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_KEY_UP:
|
||||
if (event->key.key == SDLK_LSHIFT)
|
||||
{
|
||||
DebugOverlay::show_debug_window = !DebugOverlay::show_debug_window;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
break;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
if (event->button.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
device->HandleTap(&event->button);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
if (event->button.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
device->ReleaseTap(&event->button);
|
||||
}
|
||||
|
||||
default: {
|
||||
if (DebugOverlay::show_unhandled_events)
|
||||
{
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Unused event: %d", event->type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// return continue to continue
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
const auto context = static_cast<AppContext *>(appstate);
|
||||
|
||||
/// render main window
|
||||
SDL_SetRenderDrawColor(context->MainRenderer(), 0, 0, 0, 255);
|
||||
SDL_RenderClear(context->MainRenderer());
|
||||
|
||||
const uint64_t currentTime = SDL_GetTicks();
|
||||
uint64_t dt = 0;
|
||||
|
||||
if (lastFrameTime > 0) {
|
||||
dt = currentTime - lastFrameTime;
|
||||
}
|
||||
lastFrameTime = currentTime;
|
||||
|
||||
for (const auto &widget : widgets)
|
||||
{
|
||||
widget->Render(dt);
|
||||
}
|
||||
DebugOverlay::Render(context);
|
||||
|
||||
SDL_RenderPresent(context->MainRenderer());
|
||||
|
||||
/// render led matrix
|
||||
context->Render();
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void SDL_AppQuit(void *appstate, SDL_AppResult result)
|
||||
{
|
||||
DebugOverlay::Cleanup();
|
||||
|
||||
free(appstate);
|
||||
|
||||
// SDL will clean up the window/renderer for us.
|
||||
TTF_Quit();
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
#include "ResourceManager.h"
|
||||
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
|
||||
ResourceManager &ResourceManager::Instance()
|
||||
{
|
||||
static ResourceManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
ResourceManager::ResourceManager()
|
||||
{
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "ResourceManager instance created.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for the ResourceManager
|
||||
* Cleans up all loaded textures and frees memory
|
||||
*/
|
||||
ResourceManager::~ResourceManager()
|
||||
{
|
||||
for (const auto &texture : m_textures | std::views::values)
|
||||
{
|
||||
if (texture != nullptr)
|
||||
{
|
||||
SDL_DestroyTexture(texture);
|
||||
}
|
||||
}
|
||||
m_textures.clear();
|
||||
}
|
||||
|
||||
std::string ResourceManager::GetResourcePath(const std::string &fileName)
|
||||
{
|
||||
const auto basePath_c = SDL_GetBasePath();
|
||||
if (!basePath_c)
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error retrieving base path: %s", SDL_GetError());
|
||||
// Fallback to relative path as a last resort
|
||||
// Warning: This only works reliably if the working directory is correct
|
||||
// (e.g., when debugging from IDE)
|
||||
return fileName; // Fallback (unsafe)
|
||||
}
|
||||
|
||||
const std::string basePath(basePath_c);
|
||||
|
||||
std::string fullPath = basePath;
|
||||
#ifdef __APPLE__
|
||||
// Special handling for macOS bundle structure
|
||||
// Navigate from /Contents/MacOS/ to /Contents/Resources/
|
||||
size_t lastSlash = fullPath.find_last_of('/');
|
||||
if (lastSlash != std::string::npos)
|
||||
{
|
||||
fullPath = fullPath.substr(0, lastSlash); // Remove executable name
|
||||
lastSlash = fullPath.find_last_of('/');
|
||||
if (lastSlash != std::string::npos)
|
||||
{
|
||||
fullPath = fullPath.substr(0, lastSlash + 1); // Go to Contents folder
|
||||
fullPath += "Resources/";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback if structure is unexpected
|
||||
fullPath = basePath; // Use original base path
|
||||
fullPath += "Resources/"; // And try with this
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback for unexpected path structure
|
||||
fullPath = basePath;
|
||||
fullPath += "Resources/";
|
||||
}
|
||||
#else
|
||||
// For other platforms (Windows, Linux, etc.)
|
||||
// Use standard assets folder
|
||||
fullPath += "assets/";
|
||||
#endif
|
||||
|
||||
return fullPath + fileName;
|
||||
}
|
||||
|
||||
|
||||
SDL_Texture *ResourceManager::GetTextureByName(SDL_Renderer *renderer, const std::string &path)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
// Check if texture is already in cache
|
||||
if (const auto search = m_textures.find(GetResourcePath(path)); search != m_textures.end())
|
||||
{
|
||||
return search->second;
|
||||
}
|
||||
|
||||
// Load new texture
|
||||
SDL_Texture *texture = IMG_LoadTexture(renderer, GetResourcePath(path).c_str());
|
||||
|
||||
if (!texture)
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load %s -> %s", GetResourcePath(path).c_str(),
|
||||
SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Store texture in cache
|
||||
m_textures[path] = texture;
|
||||
return texture;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
class ResourceManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the singleton instance of the ResourceManager
|
||||
* @return Reference to the ResourceManager instance
|
||||
*/
|
||||
static ResourceManager &Instance();
|
||||
|
||||
ResourceManager(const ResourceManager &) = delete;
|
||||
ResourceManager &operator=(const ResourceManager &) = delete;
|
||||
ResourceManager(ResourceManager &&) = delete;
|
||||
ResourceManager &operator=(ResourceManager &&) = delete;
|
||||
|
||||
~ResourceManager();
|
||||
|
||||
/**
|
||||
* @brief Loads a texture or returns an already loaded texture
|
||||
* @param renderer The SDL_Renderer used to load the texture
|
||||
* @param path Path to the texture file (relative to resource directory)
|
||||
* @return Pointer to the loaded SDL_Texture or nullptr on error
|
||||
*
|
||||
* This function implements a caching system for textures. If a texture
|
||||
* has already been loaded, it returns it from the cache. Otherwise,
|
||||
* it loads the texture and stores it in the cache. Access is thread-safe
|
||||
* through mutex protection.
|
||||
*/
|
||||
SDL_Texture *GetTextureByName(SDL_Renderer *renderer, const std::string &path);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Constructor for the ResourceManager
|
||||
* Initializes a new instance and logs its creation
|
||||
*/
|
||||
ResourceManager();
|
||||
|
||||
/**
|
||||
* @brief Determines the full resource path for a file
|
||||
* @param fileName Name of the resource file
|
||||
* @return Complete path to the resource
|
||||
*
|
||||
* This function handles platform-specific paths (especially macOS) and
|
||||
* constructs the correct path to resources. For macOS, it considers the
|
||||
* bundle structure (.app/Contents/Resources/).
|
||||
*/
|
||||
static std::string GetResourcePath(const std::string &fileName);
|
||||
|
||||
std::unordered_map<std::string, SDL_Texture *> m_textures;
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "model/AppContext.h"
|
||||
|
||||
#include "Matrix.h"
|
||||
|
||||
auto AppContext::MainWindow() const -> SDL_Window *
|
||||
{
|
||||
return m_window->window();
|
||||
}
|
||||
|
||||
auto AppContext::MainRenderer() const -> SDL_Renderer *
|
||||
{
|
||||
return m_window->renderer();
|
||||
}
|
||||
|
||||
auto AppContext::MainSurface() const -> SDL_Surface *
|
||||
{
|
||||
return SDL_GetWindowSurface(m_window->window());
|
||||
}
|
||||
|
||||
void AppContext::SetMatrix(Matrix *matrix)
|
||||
{
|
||||
m_matrix = matrix;
|
||||
}
|
||||
|
||||
auto AppContext::LedMatrix() const -> Matrix *
|
||||
{
|
||||
return m_matrix;
|
||||
}
|
||||
|
||||
auto AppContext::LedMatrixId() const -> SDL_WindowID
|
||||
{
|
||||
if (m_matrix)
|
||||
{
|
||||
return m_matrix->windowId();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto AppContext::LedMatrixRenderer() const -> SDL_Renderer *
|
||||
{
|
||||
if (m_matrix && m_matrix->renderer())
|
||||
{
|
||||
return m_matrix->renderer();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AppContext::Render() const
|
||||
{
|
||||
if (m_matrix && m_matrix->renderer())
|
||||
{
|
||||
m_matrix->Render();
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "SDL3_ttf/SDL_ttf.h"
|
||||
#include "Window.h"
|
||||
|
||||
class Matrix;
|
||||
|
||||
class AppContext
|
||||
{
|
||||
public:
|
||||
explicit AppContext(const Window *window) : m_window(window)
|
||||
{
|
||||
m_font_default = TTF_OpenFont("haxrcorp-4089.otf", 21);
|
||||
m_font_text = TTF_OpenFont("Helvetica-Bold.otf", 21);
|
||||
}
|
||||
|
||||
~AppContext()
|
||||
{
|
||||
TTF_CloseFont(m_font_default);
|
||||
TTF_CloseFont(m_font_text);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto MainWindow() const -> SDL_Window *;
|
||||
|
||||
[[nodiscard]] auto MainRenderer() const -> SDL_Renderer *;
|
||||
|
||||
[[nodiscard]] auto MainSurface() const -> SDL_Surface *;
|
||||
|
||||
void SetMatrix(Matrix *matrix);
|
||||
|
||||
[[nodiscard]] auto LedMatrix() const -> Matrix *;
|
||||
|
||||
[[nodiscard]] auto LedMatrixId() const -> SDL_WindowID;
|
||||
|
||||
[[nodiscard]] auto LedMatrixRenderer() const -> SDL_Renderer *;
|
||||
|
||||
void Render() const;
|
||||
|
||||
TTF_Font *m_font_default = nullptr;
|
||||
|
||||
private:
|
||||
const Window *m_window;
|
||||
Matrix *m_matrix = nullptr;
|
||||
TTF_Font *m_font_text = nullptr;
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "model/Window.h"
|
||||
|
||||
auto Window::window() const -> SDL_Window *
|
||||
{
|
||||
return m_window;
|
||||
}
|
||||
|
||||
auto Window::renderer() const -> SDL_Renderer *
|
||||
{
|
||||
return SDL_GetRenderer(m_window);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "SDL3/SDL.h"
|
||||
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
explicit Window(SDL_Window *window) : m_window(window)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] auto window() const -> SDL_Window *;
|
||||
|
||||
[[nodiscard]] auto renderer() const -> SDL_Renderer *;
|
||||
|
||||
private:
|
||||
SDL_Window *m_window = nullptr;
|
||||
};
|
||||
@@ -1,277 +0,0 @@
|
||||
#include "ui/Device.h"
|
||||
|
||||
#include <hal/u8g2_hal_sdl.h>
|
||||
#include <u8g2.h>
|
||||
|
||||
#include "MenuOptions.h"
|
||||
#include "common/InactivityTracker.h"
|
||||
#include "hal_native/PersistenceManager.h"
|
||||
#include "ui/ClockScreenSaver.h"
|
||||
#include "ui/ScreenSaver.h"
|
||||
#include "ui/SplashScreen.h"
|
||||
#include "ui/widgets/Button.h"
|
||||
#include "ui/widgets/D_Pad.h"
|
||||
|
||||
u8g2_t u8g2;
|
||||
menu_options_t options;
|
||||
std::unique_ptr<InactivityTracker> m_inactivityTracker;
|
||||
|
||||
static void set_pixel_rgba(const SDL_Surface *surface, const int x, const int y, const uint32_t pixel_color)
|
||||
{
|
||||
if (!surface || x < 0 || x >= surface->w || y < 0 || y >= surface->h)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto p = static_cast<uint8_t *>(surface->pixels) + y * surface->pitch + x * sizeof(uint32_t);
|
||||
*reinterpret_cast<uint32_t *>(p) = pixel_color;
|
||||
}
|
||||
|
||||
Device::Device(void *appstate) : UIWidget(appstate)
|
||||
{
|
||||
auto dpad_callback = [](const D_Pad::Direction direction) {
|
||||
SDL_Keycode key = 0;
|
||||
switch (direction)
|
||||
{
|
||||
case D_Pad::Direction::UP:
|
||||
key = SDLK_UP;
|
||||
break;
|
||||
case D_Pad::Direction::DOWN:
|
||||
key = SDLK_DOWN;
|
||||
break;
|
||||
case D_Pad::Direction::LEFT:
|
||||
key = SDLK_LEFT;
|
||||
break;
|
||||
case D_Pad::Direction::RIGHT:
|
||||
key = SDLK_RIGHT;
|
||||
break;
|
||||
case D_Pad::Direction::NONE: // Fallthrough oder keine Aktion
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (key != 0)
|
||||
{
|
||||
PushKey(key);
|
||||
}
|
||||
};
|
||||
|
||||
m_children.push_back(std::make_shared<Button>(
|
||||
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING + DPAD_WIDTH,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - BUTTON_WIDTH, BUTTON_WIDTH,
|
||||
[]() { PushKey(SDLK_RETURN); }));
|
||||
m_children.push_back(std::make_shared<Button>(
|
||||
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 4 * U8G2_SCREEN_PADDING + DPAD_WIDTH + BUTTON_WIDTH,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - 2 * BUTTON_WIDTH, BUTTON_WIDTH,
|
||||
[]() { PushKey(SDLK_BACKSPACE); }));
|
||||
m_children.push_back(std::make_shared<D_Pad>(
|
||||
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 2 * U8G2_SCREEN_PADDING,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - DPAD_WIDTH, DPAD_WIDTH, dpad_callback));
|
||||
|
||||
u8g2_Setup_sh1106_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sdl_hw_spi, u8x8_gpio_and_delay_sdl);
|
||||
u8x8_InitDisplay(u8g2_GetU8x8(&u8g2));
|
||||
|
||||
options = {
|
||||
.u8g2 = &u8g2,
|
||||
.setScreen = [this](const std::shared_ptr<Widget> &screen) { this->SetScreen(screen); },
|
||||
.pushScreen = [this](const std::shared_ptr<Widget> &screen) { this->PushScreen(screen); },
|
||||
.popScreen = [this]() { this->PopScreen(); },
|
||||
.persistenceManager = std::make_shared<PersistenceManager>(),
|
||||
};
|
||||
options.persistenceManager->Load();
|
||||
|
||||
m_widget = std::make_shared<SplashScreen>(&options);
|
||||
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
||||
const auto screensaver = std::make_shared<ClockScreenSaver>(&options);
|
||||
options.pushScreen(screensaver);
|
||||
});
|
||||
}
|
||||
|
||||
void Device::SetScreen(const std::shared_ptr<Widget> &screen)
|
||||
{
|
||||
if (screen != nullptr)
|
||||
{
|
||||
m_widget = screen;
|
||||
m_history.clear();
|
||||
m_history.emplace_back(m_widget);
|
||||
m_widget->onEnter();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::PushScreen(const std::shared_ptr<Widget> &screen)
|
||||
{
|
||||
if (screen != nullptr)
|
||||
{
|
||||
if (m_widget)
|
||||
{
|
||||
m_widget->onPause();
|
||||
}
|
||||
m_widget = screen;
|
||||
m_history.emplace_back(m_widget);
|
||||
m_widget->onEnter();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::PopScreen()
|
||||
{
|
||||
if (m_history.size() >= 2)
|
||||
{
|
||||
if (m_widget)
|
||||
{
|
||||
m_widget->onExit();
|
||||
}
|
||||
m_history.pop_back();
|
||||
m_widget = m_history.back();
|
||||
m_widget->onResume();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::PushKey(const SDL_Keycode key)
|
||||
{
|
||||
SDL_Event ev;
|
||||
ev.type = SDL_EVENT_KEY_DOWN;
|
||||
ev.key.key = key;
|
||||
SDL_PushEvent(&ev);
|
||||
}
|
||||
|
||||
void Device::RenderU8G2() const
|
||||
{
|
||||
SDL_Surface *u8g2_surface = nullptr;
|
||||
SDL_Texture *u8g2_texture = nullptr;
|
||||
|
||||
u8g2_surface = SDL_CreateSurface(U8G2_SCREEN_WIDTH, U8G2_SCREEN_HEIGHT, SDL_PIXELFORMAT_RGBA8888);
|
||||
|
||||
if (!u8g2_surface)
|
||||
{
|
||||
SDL_Log("SDL_CreateSurfaceFrom Error: %s\n", SDL_GetError());
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto color_black = SDL_MapSurfaceRGBA(u8g2_surface, 0, 0, 0, 255);
|
||||
const auto color_white = SDL_MapSurfaceRGBA(u8g2_surface, 255, 255, 255, 255);
|
||||
|
||||
const auto u8g2_buf = u8g2_GetBufferPtr(&u8g2);
|
||||
for (auto y = 0; y < U8G2_SCREEN_HEIGHT; ++y)
|
||||
{
|
||||
for (auto x = 0; x < U8G2_SCREEN_WIDTH; ++x)
|
||||
{
|
||||
const auto page = y / 8;
|
||||
const auto bit_index = y % 8;
|
||||
const auto byte_ptr = u8g2_buf + page * U8G2_SCREEN_WIDTH + x;
|
||||
const auto pixel_is_set = (*byte_ptr >> bit_index) & 0x01;
|
||||
set_pixel_rgba(u8g2_surface, x, y, pixel_is_set ? color_white : color_black);
|
||||
}
|
||||
}
|
||||
|
||||
u8g2_texture = SDL_CreateTextureFromSurface(GetContext()->MainRenderer(), u8g2_surface);
|
||||
if (!u8g2_texture)
|
||||
{
|
||||
SDL_Log("SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
|
||||
}
|
||||
if (!SDL_SetTextureScaleMode(u8g2_texture, SDL_SCALEMODE_NEAREST))
|
||||
{
|
||||
SDL_Log("SDL_SetTextureScaleMode Error: %s\n", SDL_GetError());
|
||||
}
|
||||
SDL_DestroySurface(u8g2_surface);
|
||||
}
|
||||
|
||||
if (u8g2_texture)
|
||||
{
|
||||
SDL_FRect destRect;
|
||||
destRect.x = U8G2_SCREEN_PADDING;
|
||||
destRect.y = U8G2_SCREEN_PADDING;
|
||||
destRect.w = U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR;
|
||||
destRect.h = U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR;
|
||||
|
||||
SDL_RenderTexture(GetContext()->MainRenderer(), u8g2_texture, nullptr, &destRect);
|
||||
SDL_DestroyTexture(u8g2_texture);
|
||||
}
|
||||
}
|
||||
|
||||
void Device::DrawBackground() const
|
||||
{
|
||||
int windowWidth = 0;
|
||||
int windowHeight = 0;
|
||||
|
||||
if (!SDL_GetWindowSize(GetContext()->MainWindow(), &windowWidth, &windowHeight))
|
||||
{
|
||||
SDL_Log("SDL_GetWindowSize Error: %s\n", SDL_GetError());
|
||||
}
|
||||
const auto rect = SDL_FRect{0.0f, 0.0f, static_cast<float>(windowWidth), static_cast<float>(windowHeight)};
|
||||
SDL_SetRenderDrawColor(GetContext()->MainRenderer(), 193, 46, 31, 255);
|
||||
SDL_RenderFillRect(GetContext()->MainRenderer(), &rect);
|
||||
}
|
||||
|
||||
void Device::DrawScreen(const uint64_t dt) const
|
||||
{
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
|
||||
if (m_widget != nullptr)
|
||||
{
|
||||
m_widget->Update(dt);
|
||||
m_widget->Render();
|
||||
}
|
||||
|
||||
RenderU8G2();
|
||||
}
|
||||
|
||||
void Device::Render(const uint64_t dt) const
|
||||
{
|
||||
DrawBackground();
|
||||
DrawScreen(dt);
|
||||
|
||||
for (const auto &child : m_children)
|
||||
{
|
||||
child->Render(dt);
|
||||
}
|
||||
|
||||
m_inactivityTracker->update(dt);
|
||||
}
|
||||
|
||||
void Device::HandleTap(const SDL_MouseButtonEvent *event) const
|
||||
{
|
||||
// SDL_Log("HandleTap: x=%f, y=%f, button=%d", event->x, event->y, event->button);
|
||||
|
||||
for (const auto &child : m_children)
|
||||
{
|
||||
if (child->IsHit(static_cast<int>(event->x), static_cast<int>(event->y)))
|
||||
{
|
||||
child->OnTap(static_cast<int>(event->x), static_cast<int>(event->y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Device::ReleaseTap(const SDL_MouseButtonEvent *event) const
|
||||
{
|
||||
// SDL_Log("ReleaseTap: x=%f, y=%f, button=%d", event->x, event->y, event->button);
|
||||
m_inactivityTracker->reset();
|
||||
|
||||
for (const auto &child : m_children)
|
||||
{
|
||||
child->ReleaseTap(static_cast<int>(event->x), static_cast<int>(event->y));
|
||||
}
|
||||
}
|
||||
|
||||
void Device::OnButtonClicked(const ButtonType button) const
|
||||
{
|
||||
m_inactivityTracker->reset();
|
||||
|
||||
if (m_widget != nullptr)
|
||||
{
|
||||
m_widget->OnButtonClicked(button);
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::IsHit(int mouse_x, int mouse_y) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Device::OnTap(int mouse_x, int mouse_y)
|
||||
{
|
||||
////
|
||||
}
|
||||
|
||||
void Device::ReleaseTap(int mouse_x, int mouse_y)
|
||||
{
|
||||
///
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "UIWidget.h"
|
||||
#include "common/Common.h"
|
||||
#include "common/Widget.h"
|
||||
#include "model/AppContext.h"
|
||||
|
||||
class Device final : public UIWidget
|
||||
{
|
||||
public:
|
||||
explicit Device(void *appstate);
|
||||
|
||||
void Render(uint64_t dt) const override;
|
||||
|
||||
void HandleTap(const SDL_MouseButtonEvent *event) const;
|
||||
|
||||
void ReleaseTap(const SDL_MouseButtonEvent *event) const;
|
||||
|
||||
void OnButtonClicked(ButtonType button) const;
|
||||
|
||||
[[nodiscard]] bool IsHit(int mouse_x, int mouse_y) const override;
|
||||
|
||||
void OnTap(int mouse_x, int mouse_y) override;
|
||||
|
||||
void ReleaseTap(int mouse_x, int mouse_y) override;
|
||||
|
||||
private:
|
||||
void DrawBackground() const;
|
||||
|
||||
void DrawScreen(uint64_t dt) const;
|
||||
|
||||
void RenderU8G2() const;
|
||||
|
||||
void SetScreen(const std::shared_ptr<Widget> &screen);
|
||||
|
||||
void PushScreen(const std::shared_ptr<Widget> &screen);
|
||||
|
||||
void PopScreen();
|
||||
|
||||
static void PushKey(SDL_Keycode key);
|
||||
|
||||
std::vector<std::shared_ptr<UIWidget>> m_children{};
|
||||
std::shared_ptr<Widget> m_widget;
|
||||
std::vector<std::shared_ptr<Widget>> m_history;
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
#include "ui/UIWidget.h"
|
||||
|
||||
UIWidget::UIWidget(void *appstate) : m_context(static_cast<AppContext *>(appstate))
|
||||
{
|
||||
}
|
||||
|
||||
UIWidget::~UIWidget() = default;
|
||||
|
||||
auto UIWidget::GetContext() const -> AppContext *
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "model/AppContext.h"
|
||||
|
||||
class UIWidget
|
||||
{
|
||||
public:
|
||||
explicit UIWidget(void *appstate);
|
||||
|
||||
virtual ~UIWidget();
|
||||
|
||||
virtual void Render(uint64_t dt) const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool IsHit(int mouse_x, int mouse_y) const = 0;
|
||||
|
||||
virtual void OnTap(int mouse_x, int mouse_y) = 0;
|
||||
|
||||
virtual void ReleaseTap(int mouse_x, int mouse_y) = 0;
|
||||
|
||||
[[nodiscard]] AppContext *GetContext() const;
|
||||
|
||||
private:
|
||||
AppContext *m_context{};
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "ui/widgets/Button.h"
|
||||
|
||||
#include "../../manager/ResourceManager.h"
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
Button::Button(void *appState, const float x, const float y, const float width, std::function<void()> callback)
|
||||
: UIWidget(appState), m_x(x), m_y(y), m_width(width), m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
void Button::Render(const uint64_t dt) const
|
||||
{
|
||||
const auto button =
|
||||
ResourceManager::Instance().GetTextureByName(GetContext()->MainRenderer(), "button_normal.png");
|
||||
const auto dst = SDL_FRect(m_x, m_y, m_width, m_width);
|
||||
SDL_RenderTexture(GetContext()->MainRenderer(), button, nullptr, &dst);
|
||||
SDL_DestroyTexture(button);
|
||||
|
||||
if (m_isPressed)
|
||||
{
|
||||
const auto overlay =
|
||||
ResourceManager::Instance().GetTextureByName(GetContext()->MainRenderer(), "button_pressed_overlay.png");
|
||||
SDL_RenderTexture(GetContext()->MainRenderer(), overlay, nullptr, &dst);
|
||||
SDL_DestroyTexture(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
bool Button::IsHit(const int mouse_x, const int mouse_y) const
|
||||
{
|
||||
const auto fx = static_cast<float>(mouse_x);
|
||||
const auto fy = static_cast<float>(mouse_y);
|
||||
return (fx >= m_x && fx <= (m_x + m_width) &&
|
||||
fy >= m_y && fy <= (m_y + m_width)) || m_isPressed;
|
||||
}
|
||||
|
||||
void Button::OnTap(int mouse_x, int mouse_y)
|
||||
{
|
||||
if (m_callback)
|
||||
{
|
||||
m_isPressed = true;
|
||||
m_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::ReleaseTap(int mouse_x, int mouse_y)
|
||||
{
|
||||
m_isPressed = false;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/UIWidget.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define BUTTON_WIDTH (35)
|
||||
|
||||
class Button final : public UIWidget
|
||||
{
|
||||
public:
|
||||
Button(void *appState, float x, float y, float width, std::function<void()> callback);
|
||||
|
||||
void Render(uint64_t dt) const override;
|
||||
|
||||
[[nodiscard]] bool IsHit(int mouse_x, int mouse_y) const override;
|
||||
|
||||
void OnTap(int mouse_x, int mouse_y) override;
|
||||
|
||||
void ReleaseTap(int mouse_x, int mouse_y) override;
|
||||
|
||||
private:
|
||||
float m_x;
|
||||
float m_y;
|
||||
float m_width;
|
||||
std::function<void()> m_callback;
|
||||
bool m_isPressed = false;
|
||||
};
|
||||
@@ -1,112 +0,0 @@
|
||||
#include "D_Pad.h"
|
||||
#include "ui/widgets/D_Pad.h"
|
||||
|
||||
#include "../../manager/ResourceManager.h"
|
||||
|
||||
D_Pad::D_Pad(void *appState, const float x, const float y, const float width, std::function<void(Direction)> callback)
|
||||
: UIWidget(appState), m_x(x), m_y(y), m_width(width), m_callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
void D_Pad::Render(const uint64_t dt) const
|
||||
{
|
||||
const auto dPad =
|
||||
ResourceManager::Instance().GetTextureByName(GetContext()->MainRenderer(), "d-pad_normal.png");
|
||||
|
||||
auto dst = SDL_FRect(m_x, m_y, m_width, m_width);
|
||||
SDL_RenderTexture(GetContext()->MainRenderer(), dPad, nullptr, &dst);
|
||||
SDL_DestroyTexture(dPad);
|
||||
|
||||
if (m_isPressed != Direction::NONE)
|
||||
{
|
||||
const auto overlay =
|
||||
ResourceManager::Instance().GetTextureByName(GetContext()->MainRenderer(), "button_pressed_overlay.png");
|
||||
switch (m_isPressed)
|
||||
{
|
||||
case Direction::UP:
|
||||
dst = SDL_FRect(m_x + m_width / 3.0f, m_y, m_width / 3.0f, m_width / 3.0f);
|
||||
break;
|
||||
|
||||
case Direction::DOWN:
|
||||
dst = SDL_FRect(m_x + m_width / 3.0f, m_y + 2 * m_width / 3.0f, m_width / 3.0f, m_width / 3.0f);
|
||||
break;
|
||||
|
||||
case Direction::LEFT:
|
||||
dst = SDL_FRect(m_x, m_y + m_width / 3.0f, m_width / 3.0f, m_width / 3.0f);
|
||||
break;
|
||||
|
||||
case Direction::RIGHT:
|
||||
dst = SDL_FRect(m_x + 2 * m_width / 3.0f, m_y + m_width / 3.0f, m_width / 3.0f, m_width / 3.0f);
|
||||
break;
|
||||
|
||||
case Direction::NONE:
|
||||
break;
|
||||
}
|
||||
SDL_RenderTexture(GetContext()->MainRenderer(), overlay, nullptr, &dst);
|
||||
SDL_DestroyTexture(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
bool D_Pad::IsHit(const int mouse_x, const int mouse_y) const
|
||||
{
|
||||
const auto fx = static_cast<float>(mouse_x);
|
||||
const auto fy = static_cast<float>(mouse_y);
|
||||
return (fx >= m_x && fx <= (m_x + m_width) &&
|
||||
fy >= m_y && fy <= (m_y + m_width));
|
||||
}
|
||||
|
||||
D_Pad::Direction D_Pad::GetDirectionFromTap(const float local_x, const float local_y) const
|
||||
{
|
||||
const float segment = m_width / 3.0f;
|
||||
|
||||
int col = -1;
|
||||
if (local_x < segment)
|
||||
col = 0;
|
||||
else if (local_x < 2 * segment)
|
||||
col = 1;
|
||||
else if (local_x <= m_width)
|
||||
col = 2;
|
||||
else
|
||||
return Direction::NONE;
|
||||
|
||||
int row = -1;
|
||||
if (local_y < segment)
|
||||
row = 0;
|
||||
else if (local_y < 2 * segment)
|
||||
row = 1;
|
||||
else if (local_y <= m_width)
|
||||
row = 2;
|
||||
else
|
||||
return Direction::NONE;
|
||||
|
||||
if (col == 1 && row == 0)
|
||||
return Direction::UP;
|
||||
if (col == 1 && row == 2)
|
||||
return Direction::DOWN;
|
||||
if (col == 0 && row == 1)
|
||||
return Direction::LEFT;
|
||||
if (col == 2 && row == 1)
|
||||
return Direction::RIGHT;
|
||||
|
||||
return Direction::NONE;
|
||||
}
|
||||
|
||||
void D_Pad::OnTap(const int mouse_x, const int mouse_y)
|
||||
{
|
||||
if (m_callback)
|
||||
{
|
||||
const auto local_x = static_cast<float>(mouse_x) - m_x;
|
||||
|
||||
if (const auto local_y = static_cast<float>(mouse_y) - m_y;
|
||||
local_x >= 0 && local_x <= m_width && local_y >= 0 && local_y <= m_width)
|
||||
{
|
||||
m_isPressed = GetDirectionFromTap(local_x, local_y);
|
||||
m_callback(m_isPressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void D_Pad::ReleaseTap(const int mouse_x, const int mouse_y)
|
||||
{
|
||||
m_isPressed = Direction::NONE;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "functional"
|
||||
#include "ui/UIWidget.h"
|
||||
|
||||
#define DPAD_WIDTH (105)
|
||||
|
||||
|
||||
class D_Pad final : public UIWidget
|
||||
{
|
||||
public:
|
||||
enum class Direction { NONE, UP, DOWN, LEFT, RIGHT };
|
||||
|
||||
D_Pad(void *appState, float x, float y, float width, std::function<void(Direction)> callback);
|
||||
|
||||
void Render(uint64_t dt) const override;
|
||||
|
||||
[[nodiscard]] bool IsHit(int mouse_x, int mouse_y) const override;
|
||||
|
||||
void OnTap(int mouse_x, int mouse_y) override;
|
||||
|
||||
void ReleaseTap(int mouse_x, int mouse_y) override;
|
||||
|
||||
private:
|
||||
float m_x, m_y, m_width;
|
||||
std::function<void(Direction)> m_callback;
|
||||
Direction m_isPressed = Direction::NONE;
|
||||
|
||||
[[nodiscard]] Direction GetDirectionFromTap(float local_x, float local_y) const;
|
||||
};
|
||||
Reference in New Issue
Block a user