Skip to content

Commit 5f6f9b7

Browse files
committed
Adding some test code for supporting monitor detection via xrandr and wayland. Still need to add xinerama support.
1 parent 1d7b774 commit 5f6f9b7

4 files changed

Lines changed: 880 additions & 8 deletions

File tree

demo/demo_properties.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,10 @@
1616
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
*/
1818

19-
#ifdef HAVE_CONFIG_H
20-
#include <config.h>
21-
#endif
22-
23-
#include <stdbool.h>
2419
#include <stdio.h>
2520
#include <stdlib.h>
2621
#include <uiohook.h>
2722

28-
2923
static void logger_proc(unsigned int level, void *user_data, const char *format, va_list args) {
3024
switch (level) {
3125
case LOG_LEVEL_INFO:
@@ -61,17 +55,18 @@ int main() {
6155
monitors[i].width, monitors[i].height,
6256
monitors[i].x, monitors[i].y);
6357
}
58+
free(monitors);
6459
logger(LOG_LEVEL_INFO, "\n");
6560

66-
// Retrieves the keyboard auto repeat rate.
61+
// Retrieves the keyboard auto-repeat rate.
6762
long int repeat_rate = hook_get_auto_repeat_rate();
6863
if (repeat_rate >= 0) {
6964
logger(LOG_LEVEL_INFO, "Auto Repeat Rate:\t%ld\n", repeat_rate);
7065
} else {
7166
logger(LOG_LEVEL_WARN, "Failed to acquire keyboard auto repeat rate!\n");
7267
}
7368

74-
// Retrieves the keyboard auto repeat delay.
69+
// Retrieves the keyboard auto-repeat delay.
7570
long int repeat_delay = hook_get_auto_repeat_delay();
7671
if (repeat_delay >= 0) {
7772
logger(LOG_LEVEL_INFO, "Auto Repeat Delay:\t%ld\n", repeat_delay);

src/evdev/test_wayland.c

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// Build: gcc -o test_wayland test_wayland.c -pthread -lwayland-client
2+
3+
#include <errno.h>
4+
#include <poll.h>
5+
#include <pthread.h>
6+
#include <signal.h>
7+
#include <stdbool.h>
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
#include <string.h>
11+
#include <wayland-client.h>
12+
13+
static volatile sig_atomic_t running = 1;
14+
15+
static void handle_sigint(int signum) {
16+
running = 0;
17+
}
18+
19+
20+
struct wlOutput {
21+
int x;
22+
int y;
23+
24+
int width;
25+
int height;
26+
27+
uint32_t registry_name; // Track the registry name
28+
struct wl_output *wl_output; // Track the wl_output object
29+
};
30+
31+
32+
struct wlContext {
33+
/* Globals */
34+
struct wl_display *wl_display;
35+
struct wl_registry *wl_registry;
36+
37+
/* Monitors */
38+
struct wlOutput *outputs;
39+
size_t count;
40+
};
41+
42+
43+
44+
static void output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform) {
45+
struct wlOutput *output = data;
46+
if (!output) {
47+
printf("!!! Output not found\n");
48+
return;
49+
}
50+
51+
output->x = x;
52+
output->y = y;
53+
printf("WL: output at position %d,%d\n", x, y);
54+
}
55+
56+
static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
57+
struct wlOutput *output = data;
58+
bool preferred = flags & WL_OUTPUT_MODE_PREFERRED;
59+
bool current = flags & WL_OUTPUT_MODE_CURRENT;
60+
61+
printf("WL: %smode: %dx%d@%d%s\n", current ? "current " : "", width, height, refresh, preferred ? "*" : "");
62+
if (!output) {
63+
printf("!!! Output not found in list\n");
64+
return;
65+
}
66+
67+
if (current) {
68+
if (!preferred) {
69+
printf("Not using preferred mode on output -- check config\n");
70+
}
71+
72+
output->width = width;
73+
output->height = height;
74+
}
75+
}
76+
77+
static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) {
78+
// Do Nothing
79+
}
80+
81+
static void output_done(void *data, struct wl_output *wl_output) {
82+
struct wlOutput *output = data;
83+
if (!output) {
84+
printf("!!! Output not found in list\n");
85+
return;
86+
}
87+
88+
printf("Output updated: %dx%d at %d, %d\n",
89+
output->width,
90+
output->height,
91+
output->x,
92+
output->y);
93+
}
94+
95+
static struct wl_output_listener output_listener = {
96+
.geometry = output_geometry,
97+
.mode = output_mode,
98+
.done = output_done,
99+
.scale = output_scale
100+
};
101+
102+
static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
103+
struct wlContext *ctx = data;
104+
105+
if (strcmp(interface, wl_output_interface.name) == 0) {
106+
struct wlOutput *outputs = ctx->outputs;
107+
108+
if (outputs == NULL) {
109+
outputs = malloc(sizeof(struct wlOutput));
110+
} else {
111+
outputs = realloc(ctx->outputs, sizeof(struct wlOutput) * (ctx->count + 1));
112+
}
113+
114+
if (outputs) {
115+
// Received monitor information, setup new listeners
116+
struct wl_output *wl_output = wl_registry_bind(registry, name, &wl_output_interface, 2);
117+
outputs[ctx->count].registry_name = name; // Store the registry name
118+
outputs[ctx->count].wl_output = wl_output; // Store the wl_output object
119+
wl_output_add_listener(wl_output, &output_listener, &outputs[ctx->count++]);
120+
ctx->outputs = outputs;
121+
}
122+
}
123+
}
124+
125+
static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
126+
struct wlContext *ctx = data;
127+
128+
// Find the output with this registry name
129+
for (size_t i = 0; i < ctx->count; i++) {
130+
if (ctx->outputs[i].registry_name == name) {
131+
printf("Output removed: %dx%d at %d, %d\n",
132+
ctx->outputs[i].width,
133+
ctx->outputs[i].height,
134+
ctx->outputs[i].x,
135+
ctx->outputs[i].y);
136+
137+
// Clean up the wl_output object
138+
if (ctx->outputs[i].wl_output) {
139+
wl_output_destroy(ctx->outputs[i].wl_output);
140+
}
141+
142+
// Remove from array by shifting remaining elements
143+
ctx->count--;
144+
if (i < ctx->count) {
145+
memcpy(&ctx->outputs[i], &ctx->outputs[i + 1], sizeof(struct wlOutput) * (ctx->count - i));
146+
}
147+
148+
// Optionally realloc to shrink the array
149+
if (ctx->count > 0) {
150+
ctx->outputs = realloc(ctx->outputs, sizeof(struct wlOutput) * ctx->count);
151+
} else {
152+
free(ctx->outputs);
153+
ctx->outputs = NULL;
154+
}
155+
156+
break;
157+
}
158+
}
159+
}
160+
161+
static const struct wl_registry_listener wl_registry_listener = {
162+
.global = handle_global,
163+
.global_remove = handle_global_remove,
164+
};
165+
166+
static void *event_loop_thread(void *arg) {
167+
struct wlContext *ctx = arg;
168+
169+
// Main event loop with interruptible dispatch
170+
int wl_fd = wl_display_get_fd(ctx->wl_display);
171+
struct pollfd fds = {
172+
.fd = wl_fd,
173+
.events = POLLIN,
174+
};
175+
176+
while (running) {
177+
// Flush outgoing requests
178+
while (wl_display_prepare_read(ctx->wl_display) != 0) {
179+
if (wl_display_dispatch_pending(ctx->wl_display) == -1) {
180+
running = 0;
181+
break;
182+
}
183+
}
184+
185+
if (!running) {
186+
wl_display_cancel_read(ctx->wl_display);
187+
break;
188+
}
189+
190+
wl_display_flush(ctx->wl_display);
191+
192+
// Poll with timeout to allow checking the running flag periodically
193+
int ret = poll(&fds, 1, 250); // 250ms timeout
194+
if (ret == -1) {
195+
wl_display_cancel_read(ctx->wl_display);
196+
if (errno == EINTR) {
197+
continue; // Signal interrupted, check running flag
198+
}
199+
perror("poll");
200+
break;
201+
}
202+
203+
if (ret == 0) {
204+
// Timeout - no events
205+
wl_display_cancel_read(ctx->wl_display);
206+
continue;
207+
}
208+
209+
// Events available
210+
wl_display_read_events(ctx->wl_display);
211+
if (wl_display_dispatch_pending(ctx->wl_display) == -1) {
212+
break;
213+
}
214+
}
215+
216+
return NULL;
217+
}
218+
219+
int main() {
220+
// Setup signal handler for Ctrl+C
221+
struct sigaction sa = { 0 };
222+
sa.sa_handler = handle_sigint;
223+
sigaction(SIGINT, &sa, NULL);
224+
225+
// Connect to the display server
226+
struct wlContext ctx = { 0 };
227+
ctx.wl_display = wl_display_connect(NULL);
228+
if (ctx.wl_display == NULL) {
229+
fprintf(stderr, "Failed to connect to display.\n");
230+
exit(1);
231+
}
232+
233+
// Add the listener and pass state as the void *data.
234+
ctx.wl_registry = wl_display_get_registry(ctx.wl_display);
235+
wl_registry_add_listener(ctx.wl_registry, &wl_registry_listener, &ctx);
236+
wl_display_roundtrip(ctx.wl_display);
237+
238+
// Start the event loop in a separate thread
239+
pthread_t thread;
240+
if (pthread_create(&thread, NULL, event_loop_thread, &ctx) != 0) {
241+
fprintf(stderr, "Failed to create thread.\n");
242+
wl_registry_destroy(ctx.wl_registry);
243+
wl_display_disconnect(ctx.wl_display);
244+
exit(1);
245+
}
246+
247+
// Main thread blocks waiting for the event loop thread to complete
248+
pthread_join(thread, NULL);
249+
250+
printf("\nShutting down gracefully...\n");
251+
252+
wl_registry_destroy(ctx.wl_registry);
253+
254+
// Disconnect from the display server
255+
wl_display_disconnect(ctx.wl_display);
256+
257+
return 0;
258+
}

0 commit comments

Comments
 (0)