Skip to content

Commit 9ea73b5

Browse files
committed
Add C API for shortcut management and example
Introduces C API bindings for shortcut and shortcut manager functionality, including registration, event callbacks, and shortcut operations. Adds new example project 'shortcut_c_example' demonstrating usage of the C API. Updates nativeapi.h to include the new headers.
1 parent 9bdf6bd commit 9ea73b5

File tree

8 files changed

+979
-0
lines changed

8 files changed

+979
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_subdirectory(examples/menu_c_example)
1717
add_subdirectory(examples/message_dialog_example)
1818
add_subdirectory(examples/message_dialog_c_example)
1919
add_subdirectory(examples/shortcut_example)
20+
add_subdirectory(examples/shortcut_c_example)
2021
add_subdirectory(examples/storage_example)
2122
add_subdirectory(examples/storage_c_example)
2223
add_subdirectory(examples/tray_icon_example)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(shortcut_c_example)
3+
4+
set(CMAKE_C_STANDARD 11)
5+
6+
add_executable(shortcut_c_example main.c)
7+
8+
target_include_directories(shortcut_c_example PRIVATE
9+
${CMAKE_SOURCE_DIR}/include
10+
${CMAKE_SOURCE_DIR}/src/capi
11+
)
12+
13+
target_link_libraries(shortcut_c_example PRIVATE nativeapi)
14+

examples/shortcut_c_example/main.c

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#include <nativeapi.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
5+
#ifdef _WIN32
6+
#include <windows.h>
7+
#define SLEEP_MS(ms) Sleep(ms)
8+
#else
9+
#include <unistd.h>
10+
#define SLEEP_MS(ms) usleep((ms) * 1000)
11+
#endif
12+
13+
// Shortcut callback function
14+
void on_shortcut_activated(native_shortcut_id_t shortcut_id, void* user_data) {
15+
const char* name = (const char*)user_data;
16+
printf("Shortcut activated: %s (ID: %llu)\n", name, (unsigned long long)shortcut_id);
17+
}
18+
19+
// Event callback function
20+
void on_shortcut_event(const native_shortcut_event_t* event, void* user_data) {
21+
const char* event_type_name = "";
22+
23+
switch (event->type) {
24+
case NATIVE_SHORTCUT_EVENT_ACTIVATED:
25+
event_type_name = "ACTIVATED";
26+
break;
27+
case NATIVE_SHORTCUT_EVENT_REGISTERED:
28+
event_type_name = "REGISTERED";
29+
break;
30+
case NATIVE_SHORTCUT_EVENT_UNREGISTERED:
31+
event_type_name = "UNREGISTERED";
32+
break;
33+
case NATIVE_SHORTCUT_EVENT_REGISTRATION_FAILED:
34+
event_type_name = "REGISTRATION_FAILED";
35+
if (event->data.registration_failed.error_message) {
36+
printf("Shortcut event: %s - %s (ID: %llu) - Error: %s\n", event_type_name,
37+
event->accelerator, (unsigned long long)event->shortcut_id,
38+
event->data.registration_failed.error_message);
39+
return;
40+
}
41+
break;
42+
}
43+
44+
printf("Shortcut event: %s - %s (ID: %llu)\n", event_type_name, event->accelerator,
45+
(unsigned long long)event->shortcut_id);
46+
}
47+
48+
int main(void) {
49+
printf("Shortcut Manager C API Example\n");
50+
printf("==============================\n\n");
51+
52+
// Check if shortcuts are supported
53+
if (!native_shortcut_manager_is_supported()) {
54+
printf("Global shortcuts are not supported on this platform\n");
55+
return 1;
56+
}
57+
58+
printf("Global shortcuts are supported\n\n");
59+
60+
// Register event callback
61+
int event_callback_id =
62+
native_shortcut_manager_register_event_callback(on_shortcut_event, NULL);
63+
if (event_callback_id < 0) {
64+
printf("Failed to register event callback\n");
65+
return 1;
66+
}
67+
68+
printf("Event callback registered (ID: %d)\n\n", event_callback_id);
69+
70+
// Register shortcuts
71+
printf("Registering shortcuts...\n");
72+
73+
// Simple registration
74+
native_shortcut_t shortcut1 = native_shortcut_manager_register(
75+
"Ctrl+Shift+A", on_shortcut_activated, (void*)"Shortcut 1");
76+
77+
if (!shortcut1) {
78+
printf("Failed to register shortcut 1\n");
79+
} else {
80+
printf("Registered shortcut 1: %s\n", native_shortcut_get_accelerator(shortcut1));
81+
}
82+
83+
// Registration with options
84+
native_shortcut_options_t options;
85+
options.accelerator = "Ctrl+Shift+B";
86+
options.description = "Test shortcut 2";
87+
options.scope = NATIVE_SHORTCUT_SCOPE_GLOBAL;
88+
options.enabled = true;
89+
90+
native_shortcut_t shortcut2 = native_shortcut_manager_register_with_options(
91+
&options, on_shortcut_activated, (void*)"Shortcut 2");
92+
93+
if (!shortcut2) {
94+
printf("Failed to register shortcut 2\n");
95+
} else {
96+
printf("Registered shortcut 2: %s - %s\n", native_shortcut_get_accelerator(shortcut2),
97+
native_shortcut_get_description(shortcut2));
98+
}
99+
100+
// Register application-local shortcut
101+
options.accelerator = "Ctrl+Shift+C";
102+
options.description = "Application-local shortcut";
103+
options.scope = NATIVE_SHORTCUT_SCOPE_APPLICATION;
104+
105+
native_shortcut_t shortcut3 = native_shortcut_manager_register_with_options(
106+
&options, on_shortcut_activated, (void*)"Shortcut 3");
107+
108+
if (!shortcut3) {
109+
printf("Failed to register shortcut 3\n");
110+
} else {
111+
printf("Registered shortcut 3: %s (scope: %s)\n", native_shortcut_get_accelerator(shortcut3),
112+
native_shortcut_get_scope(shortcut3) == NATIVE_SHORTCUT_SCOPE_GLOBAL ? "Global"
113+
: "Application");
114+
}
115+
116+
printf("\n");
117+
118+
// Get all shortcuts
119+
native_shortcut_list_t all_shortcuts = native_shortcut_manager_get_all();
120+
printf("Total shortcuts registered: %zu\n", all_shortcuts.count);
121+
122+
for (size_t i = 0; i < all_shortcuts.count; i++) {
123+
native_shortcut_t shortcut = all_shortcuts.shortcuts[i];
124+
printf(" - %s (ID: %llu, enabled: %s)\n", native_shortcut_get_accelerator(shortcut),
125+
(unsigned long long)native_shortcut_get_id(shortcut),
126+
native_shortcut_is_enabled(shortcut) ? "yes" : "no");
127+
}
128+
129+
native_shortcut_list_free(all_shortcuts);
130+
131+
printf("\n");
132+
133+
// Get shortcuts by scope
134+
native_shortcut_list_t global_shortcuts =
135+
native_shortcut_manager_get_by_scope(NATIVE_SHORTCUT_SCOPE_GLOBAL);
136+
printf("Global shortcuts: %zu\n", global_shortcuts.count);
137+
native_shortcut_list_free(global_shortcuts);
138+
139+
native_shortcut_list_t app_shortcuts =
140+
native_shortcut_manager_get_by_scope(NATIVE_SHORTCUT_SCOPE_APPLICATION);
141+
printf("Application shortcuts: %zu\n", app_shortcuts.count);
142+
native_shortcut_list_free(app_shortcuts);
143+
144+
printf("\n");
145+
146+
// Test shortcut operations
147+
if (shortcut2) {
148+
printf("Testing shortcut operations on shortcut 2...\n");
149+
150+
// Disable shortcut
151+
printf("Disabling shortcut 2...\n");
152+
native_shortcut_set_enabled(shortcut2, false);
153+
printf("Shortcut 2 enabled: %s\n", native_shortcut_is_enabled(shortcut2) ? "yes" : "no");
154+
155+
// Re-enable shortcut
156+
printf("Re-enabling shortcut 2...\n");
157+
native_shortcut_set_enabled(shortcut2, true);
158+
printf("Shortcut 2 enabled: %s\n", native_shortcut_is_enabled(shortcut2) ? "yes" : "no");
159+
160+
// Update description
161+
native_shortcut_set_description(shortcut2, "Updated description");
162+
printf("Shortcut 2 description: %s\n", native_shortcut_get_description(shortcut2));
163+
164+
printf("\n");
165+
}
166+
167+
// Test accelerator validation
168+
printf("Testing accelerator validation...\n");
169+
printf("Is 'Ctrl+Shift+D' valid? %s\n",
170+
native_shortcut_manager_is_valid_accelerator("Ctrl+Shift+D") ? "yes" : "no");
171+
printf("Is 'InvalidKey' valid? %s\n",
172+
native_shortcut_manager_is_valid_accelerator("InvalidKey") ? "yes" : "no");
173+
printf("Is 'Ctrl+Shift+A' available? %s\n",
174+
native_shortcut_manager_is_available("Ctrl+Shift+A") ? "yes" : "no");
175+
printf("Is 'Ctrl+Shift+Z' available? %s\n",
176+
native_shortcut_manager_is_available("Ctrl+Shift+Z") ? "yes" : "no");
177+
178+
printf("\n");
179+
180+
// Wait for shortcuts to be triggered
181+
printf("Press the registered shortcuts to test them:\n");
182+
printf(" - Ctrl+Shift+A (Shortcut 1)\n");
183+
printf(" - Ctrl+Shift+B (Shortcut 2)\n");
184+
printf(" - Ctrl+Shift+C (Shortcut 3 - application-local)\n");
185+
printf("\nPress Ctrl+C to exit...\n\n");
186+
187+
// Keep the program running to receive shortcut events
188+
for (int i = 0; i < 60; i++) {
189+
SLEEP_MS(1000);
190+
}
191+
192+
// Cleanup
193+
printf("\nCleaning up...\n");
194+
195+
// Unregister shortcuts
196+
if (shortcut1) {
197+
native_shortcut_id_t id = native_shortcut_get_id(shortcut1);
198+
if (native_shortcut_manager_unregister_by_id(id)) {
199+
printf("Unregistered shortcut 1\n");
200+
}
201+
}
202+
203+
if (shortcut2) {
204+
if (native_shortcut_manager_unregister_by_accelerator("Ctrl+Shift+B")) {
205+
printf("Unregistered shortcut 2\n");
206+
}
207+
}
208+
209+
// Unregister all remaining shortcuts
210+
int count = native_shortcut_manager_unregister_all();
211+
printf("Unregistered %d remaining shortcuts\n", count);
212+
213+
// Unregister event callback
214+
if (native_shortcut_manager_unregister_event_callback(event_callback_id)) {
215+
printf("Event callback unregistered\n");
216+
}
217+
218+
printf("\nDone!\n");
219+
return 0;
220+
}
221+

include/nativeapi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
#include "../src/capi/message_dialog_c.h"
4848
#include "../src/capi/preferences_c.h"
4949
#include "../src/capi/secure_storage_c.h"
50+
#include "../src/capi/shortcut_c.h"
51+
#include "../src/capi/shortcut_manager_c.h"
5052
#include "../src/capi/string_utils_c.h"
5153
#include "../src/capi/tray_icon_c.h"
5254
#include "../src/capi/tray_manager_c.h"

src/capi/shortcut_c.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include "shortcut_c.h"
2+
#include <mutex>
3+
#include <string>
4+
#include <unordered_map>
5+
#include "../shortcut.h"
6+
7+
using namespace nativeapi;
8+
9+
// Helper to get Shortcut pointer from handle
10+
static Shortcut* GetShortcut(native_shortcut_t shortcut) {
11+
return static_cast<Shortcut*>(shortcut);
12+
}
13+
14+
// Thread-local storage for string returns
15+
static std::mutex g_string_cache_mutex;
16+
static std::unordered_map<native_shortcut_id_t, std::string> g_accelerator_cache;
17+
static std::unordered_map<native_shortcut_id_t, std::string> g_description_cache;
18+
19+
native_shortcut_id_t native_shortcut_get_id(native_shortcut_t shortcut) {
20+
if (!shortcut)
21+
return 0;
22+
return GetShortcut(shortcut)->GetId();
23+
}
24+
25+
const char* native_shortcut_get_accelerator(native_shortcut_t shortcut) {
26+
if (!shortcut)
27+
return "";
28+
29+
auto* sc = GetShortcut(shortcut);
30+
auto id = sc->GetId();
31+
auto accelerator = sc->GetAccelerator();
32+
33+
std::lock_guard<std::mutex> lock(g_string_cache_mutex);
34+
g_accelerator_cache[id] = accelerator;
35+
return g_accelerator_cache[id].c_str();
36+
}
37+
38+
const char* native_shortcut_get_description(native_shortcut_t shortcut) {
39+
if (!shortcut)
40+
return "";
41+
42+
auto* sc = GetShortcut(shortcut);
43+
auto id = sc->GetId();
44+
auto description = sc->GetDescription();
45+
46+
std::lock_guard<std::mutex> lock(g_string_cache_mutex);
47+
g_description_cache[id] = description;
48+
return g_description_cache[id].c_str();
49+
}
50+
51+
void native_shortcut_set_description(native_shortcut_t shortcut, const char* description) {
52+
if (!shortcut || !description)
53+
return;
54+
GetShortcut(shortcut)->SetDescription(description);
55+
}
56+
57+
native_shortcut_scope_t native_shortcut_get_scope(native_shortcut_t shortcut) {
58+
if (!shortcut)
59+
return NATIVE_SHORTCUT_SCOPE_GLOBAL;
60+
61+
ShortcutScope scope = GetShortcut(shortcut)->GetScope();
62+
return scope == ShortcutScope::Global ? NATIVE_SHORTCUT_SCOPE_GLOBAL
63+
: NATIVE_SHORTCUT_SCOPE_APPLICATION;
64+
}
65+
66+
void native_shortcut_set_enabled(native_shortcut_t shortcut, bool enabled) {
67+
if (!shortcut)
68+
return;
69+
GetShortcut(shortcut)->SetEnabled(enabled);
70+
}
71+
72+
bool native_shortcut_is_enabled(native_shortcut_t shortcut) {
73+
if (!shortcut)
74+
return false;
75+
return GetShortcut(shortcut)->IsEnabled();
76+
}
77+
78+
void native_shortcut_invoke(native_shortcut_t shortcut) {
79+
if (!shortcut)
80+
return;
81+
GetShortcut(shortcut)->Invoke();
82+
}
83+

0 commit comments

Comments
 (0)