Skip to content

Commit 286a24c

Browse files
authored
[C API] Implement KeyboardMonitor C API bindings (#11)
1 parent 29cc35a commit 286a24c

File tree

5 files changed

+402
-1
lines changed

5 files changed

+402
-1
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
cmake_minimum_required(VERSION 3.10)
22

3-
project(libnativeapi_library VERSION 0.0.1 LANGUAGES CXX)
3+
project(libnativeapi_library VERSION 0.0.1 LANGUAGES CXX C)
44

55
# Add library subdirectory
66
add_subdirectory(src)
77

88
# Add example programs subdirectory
99
add_subdirectory(examples/display_example)
1010
add_subdirectory(examples/window_example)
11+
add_subdirectory(examples/keyboard_example)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# The Flutter tooling requires that developers have CMake 3.10 or later
2+
# installed. You should not increase this version, as doing so will cause
3+
# the plugin to fail to compile for some customers of the plugin.
4+
cmake_minimum_required(VERSION 3.10)
5+
6+
project(keyboard_example)
7+
8+
# Set C++ standard
9+
set(CMAKE_CXX_STANDARD 17)
10+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
11+
12+
# Add executable
13+
add_executable(keyboard_example main.cpp)
14+
15+
# Link with the native API library
16+
target_link_libraries(keyboard_example libnativeapi)
17+
18+
# Set include directories to find the C API header
19+
target_include_directories(keyboard_example PRIVATE ../../src)

examples/keyboard_example/main.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include <iostream>
2+
#include <signal.h>
3+
#include <thread>
4+
#include <chrono>
5+
6+
extern "C" {
7+
#include "../../src/capi/keyboard_monitor_c.h"
8+
}
9+
10+
// Global monitor handle for cleanup
11+
static native_keyboard_monitor_t* g_monitor = nullptr;
12+
static bool g_running = true;
13+
14+
// Signal handler for graceful shutdown
15+
void signal_handler(int sig) {
16+
std::cout << "\nReceived signal " << sig << ", shutting down...\n";
17+
g_running = false;
18+
if (g_monitor) {
19+
native_keyboard_monitor_stop(g_monitor);
20+
native_keyboard_monitor_destroy(g_monitor);
21+
g_monitor = nullptr;
22+
}
23+
exit(0);
24+
}
25+
26+
// Callback for key pressed events
27+
void on_key_pressed(int keycode, void* user_data) {
28+
std::cout << "Key pressed: " << keycode << std::endl;
29+
}
30+
31+
// Callback for key released events
32+
void on_key_released(int keycode, void* user_data) {
33+
std::cout << "Key released: " << keycode << std::endl;
34+
}
35+
36+
// Callback for modifier keys changed events
37+
void on_modifier_keys_changed(uint32_t modifier_keys, void* user_data) {
38+
std::cout << "Modifier keys changed: 0x" << std::hex << modifier_keys << std::dec;
39+
40+
if (modifier_keys & NATIVE_MODIFIER_KEY_SHIFT) std::cout << " SHIFT";
41+
if (modifier_keys & NATIVE_MODIFIER_KEY_CTRL) std::cout << " CTRL";
42+
if (modifier_keys & NATIVE_MODIFIER_KEY_ALT) std::cout << " ALT";
43+
if (modifier_keys & NATIVE_MODIFIER_KEY_META) std::cout << " META";
44+
if (modifier_keys & NATIVE_MODIFIER_KEY_FN) std::cout << " FN";
45+
if (modifier_keys & NATIVE_MODIFIER_KEY_CAPS_LOCK) std::cout << " CAPS";
46+
if (modifier_keys & NATIVE_MODIFIER_KEY_NUM_LOCK) std::cout << " NUM";
47+
if (modifier_keys & NATIVE_MODIFIER_KEY_SCROLL_LOCK) std::cout << " SCROLL";
48+
49+
std::cout << std::endl;
50+
}
51+
52+
int main() {
53+
std::cout << "KeyboardMonitor C API Example\n";
54+
std::cout << "==============================\n";
55+
56+
// Set up signal handlers
57+
signal(SIGINT, signal_handler);
58+
signal(SIGTERM, signal_handler);
59+
60+
// Create keyboard monitor
61+
g_monitor = native_keyboard_monitor_create();
62+
if (!g_monitor) {
63+
std::cout << "❌ Failed to create keyboard monitor\n";
64+
return 1;
65+
}
66+
std::cout << "✅ Keyboard monitor created successfully\n";
67+
68+
// Set up callbacks
69+
if (!native_keyboard_monitor_set_callbacks(
70+
g_monitor,
71+
on_key_pressed,
72+
on_key_released,
73+
on_modifier_keys_changed,
74+
nullptr)) {
75+
std::cout << "❌ Failed to set callbacks\n";
76+
native_keyboard_monitor_destroy(g_monitor);
77+
return 1;
78+
}
79+
std::cout << "✅ Callbacks set successfully\n";
80+
81+
// Start monitoring
82+
if (!native_keyboard_monitor_start(g_monitor)) {
83+
std::cout << "❌ Failed to start keyboard monitoring\n";
84+
native_keyboard_monitor_destroy(g_monitor);
85+
return 1;
86+
}
87+
88+
if (native_keyboard_monitor_is_monitoring(g_monitor)) {
89+
std::cout << "✅ Keyboard monitoring is now active\n";
90+
std::cout << "\n📚 This example demonstrates the KeyboardMonitor C API:\n";
91+
std::cout << "• native_keyboard_monitor_create() - Creates a monitor instance\n";
92+
std::cout << "• native_keyboard_monitor_set_callbacks() - Sets event callbacks\n";
93+
std::cout << "• native_keyboard_monitor_start() - Starts monitoring\n";
94+
std::cout << "• native_keyboard_monitor_is_monitoring() - Checks status\n";
95+
std::cout << "• native_keyboard_monitor_stop() - Stops monitoring\n";
96+
std::cout << "• native_keyboard_monitor_destroy() - Cleans up resources\n";
97+
std::cout << "\n🎹 Press keys to see events. Press Ctrl+C to exit.\n\n";
98+
} else {
99+
std::cout << "⚠️ Monitor created but not monitoring (may be due to permissions or display server)\n";
100+
std::cout << "This is expected in headless environments or without proper permissions.\n";
101+
std::cout << "On a desktop system with X11/Wayland, you would see keyboard events.\n\n";
102+
}
103+
104+
// Keep the main thread alive to receive events
105+
while (g_running) {
106+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
107+
}
108+
109+
return 0;
110+
}

src/capi/keyboard_monitor_c.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include "keyboard_monitor_c.h"
2+
#include "../keyboard_monitor.h"
3+
#include <memory>
4+
5+
// Internal structure to hold C API state
6+
struct native_keyboard_monitor_t {
7+
std::unique_ptr<nativeapi::KeyboardMonitor> cpp_monitor;
8+
std::unique_ptr<nativeapi::KeyboardEventHandler> event_handler;
9+
10+
// Store C callback functions and user data
11+
native_key_pressed_callback_t on_key_pressed;
12+
native_key_released_callback_t on_key_released;
13+
native_modifier_keys_changed_callback_t on_modifier_keys_changed;
14+
void* user_data;
15+
16+
native_keyboard_monitor_t()
17+
: cpp_monitor(nullptr),
18+
event_handler(nullptr),
19+
on_key_pressed(nullptr),
20+
on_key_released(nullptr),
21+
on_modifier_keys_changed(nullptr),
22+
user_data(nullptr) {}
23+
};
24+
25+
// Helper function to convert C++ ModifierKey enum to C enum
26+
static uint32_t convert_modifier_keys_to_c(uint32_t cpp_modifier_keys) {
27+
uint32_t c_modifier_keys = 0;
28+
29+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::Shift)) {
30+
c_modifier_keys |= NATIVE_MODIFIER_KEY_SHIFT;
31+
}
32+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::Ctrl)) {
33+
c_modifier_keys |= NATIVE_MODIFIER_KEY_CTRL;
34+
}
35+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::Alt)) {
36+
c_modifier_keys |= NATIVE_MODIFIER_KEY_ALT;
37+
}
38+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::Meta)) {
39+
c_modifier_keys |= NATIVE_MODIFIER_KEY_META;
40+
}
41+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::Fn)) {
42+
c_modifier_keys |= NATIVE_MODIFIER_KEY_FN;
43+
}
44+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::CapsLock)) {
45+
c_modifier_keys |= NATIVE_MODIFIER_KEY_CAPS_LOCK;
46+
}
47+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::NumLock)) {
48+
c_modifier_keys |= NATIVE_MODIFIER_KEY_NUM_LOCK;
49+
}
50+
if (cpp_modifier_keys & static_cast<uint32_t>(nativeapi::ModifierKey::ScrollLock)) {
51+
c_modifier_keys |= NATIVE_MODIFIER_KEY_SCROLL_LOCK;
52+
}
53+
54+
return c_modifier_keys;
55+
}
56+
57+
native_keyboard_monitor_t* native_keyboard_monitor_create(void) {
58+
try {
59+
native_keyboard_monitor_t* monitor = new native_keyboard_monitor_t();
60+
monitor->cpp_monitor = std::make_unique<nativeapi::KeyboardMonitor>();
61+
return monitor;
62+
} catch (...) {
63+
return nullptr;
64+
}
65+
}
66+
67+
void native_keyboard_monitor_destroy(native_keyboard_monitor_t* monitor) {
68+
if (!monitor) {
69+
return;
70+
}
71+
72+
// Stop monitoring first if it's active
73+
if (monitor->cpp_monitor && monitor->cpp_monitor->IsMonitoring()) {
74+
monitor->cpp_monitor->Stop();
75+
}
76+
77+
delete monitor;
78+
}
79+
80+
bool native_keyboard_monitor_set_callbacks(
81+
native_keyboard_monitor_t* monitor,
82+
native_key_pressed_callback_t on_key_pressed,
83+
native_key_released_callback_t on_key_released,
84+
native_modifier_keys_changed_callback_t on_modifier_keys_changed,
85+
void* user_data) {
86+
87+
if (!monitor || !monitor->cpp_monitor) {
88+
return false;
89+
}
90+
91+
// Store the C callbacks and user data
92+
monitor->on_key_pressed = on_key_pressed;
93+
monitor->on_key_released = on_key_released;
94+
monitor->on_modifier_keys_changed = on_modifier_keys_changed;
95+
monitor->user_data = user_data;
96+
97+
try {
98+
// Create C++ lambda callbacks that call the C callbacks
99+
auto key_pressed_callback = [monitor](int keycode) {
100+
if (monitor->on_key_pressed) {
101+
monitor->on_key_pressed(keycode, monitor->user_data);
102+
}
103+
};
104+
105+
auto key_released_callback = [monitor](int keycode) {
106+
if (monitor->on_key_released) {
107+
monitor->on_key_released(keycode, monitor->user_data);
108+
}
109+
};
110+
111+
auto modifier_keys_changed_callback = [monitor](uint32_t modifier_keys) {
112+
if (monitor->on_modifier_keys_changed) {
113+
uint32_t c_modifier_keys = convert_modifier_keys_to_c(modifier_keys);
114+
monitor->on_modifier_keys_changed(c_modifier_keys, monitor->user_data);
115+
}
116+
};
117+
118+
// Create the event handler with the lambda callbacks
119+
monitor->event_handler = std::make_unique<nativeapi::KeyboardEventHandler>(
120+
key_pressed_callback,
121+
key_released_callback,
122+
modifier_keys_changed_callback);
123+
124+
// Set the event handler on the keyboard monitor
125+
monitor->cpp_monitor->SetEventHandler(monitor->event_handler.get());
126+
127+
return true;
128+
} catch (...) {
129+
return false;
130+
}
131+
}
132+
133+
bool native_keyboard_monitor_start(native_keyboard_monitor_t* monitor) {
134+
if (!monitor || !monitor->cpp_monitor) {
135+
return false;
136+
}
137+
138+
try {
139+
monitor->cpp_monitor->Start();
140+
return true;
141+
} catch (...) {
142+
return false;
143+
}
144+
}
145+
146+
bool native_keyboard_monitor_stop(native_keyboard_monitor_t* monitor) {
147+
if (!monitor || !monitor->cpp_monitor) {
148+
return false;
149+
}
150+
151+
try {
152+
monitor->cpp_monitor->Stop();
153+
return true;
154+
} catch (...) {
155+
return false;
156+
}
157+
}
158+
159+
bool native_keyboard_monitor_is_monitoring(const native_keyboard_monitor_t* monitor) {
160+
if (!monitor || !monitor->cpp_monitor) {
161+
return false;
162+
}
163+
164+
try {
165+
return monitor->cpp_monitor->IsMonitoring();
166+
} catch (...) {
167+
return false;
168+
}
169+
}

0 commit comments

Comments
 (0)