GCC Code Coverage Report


Directory: src/gate/
File: src/gate/ui/paintsurfaces.c
Date: 2026-09-21 06:26:40
Exec Total Coverage
Lines: 158 182 86.8%
Functions: 8 8 100.0%
Branches: 35 69 50.7%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright (c) 2018-2026, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/ui/paintsurfaces.h"
30 #include "gate/results.h"
31 #include "gate/debugging.h"
32
33 #if defined(GATE_UI_WINAPI)
34
35 #include "gate/ui/gateui_winapi.h"
36 #include "gate/platforms.h"
37 #include <windowsx.h>
38
39 #ifndef WM_MOUSEWHEEL
40 #define WM_MOUSEWHEEL 0x020A
41 #endif
42
43 #ifndef GET_WHEEL_DELTA_WPARAM
44 #define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam))
45 #endif
46
47 #ifndef WHEEL_DELTA
48 #define WHEEL_DELTA 120
49 #endif
50
51 #ifndef GET_X_LPARAM
52 #define GET_X_LPARAM(lparam) LOWORD(lparam)
53 #endif
54
55 #ifndef GET_Y_LPARAM
56 #define GET_Y_LPARAM(lparam) HIWORD(lparam)
57 #endif
58
59 static gate_bool_t WINAPI gate_ui_paintsurface_events(ui_hwnd_t ui_hwnd, gate_ui_ctrl_t* ctrl, gate_uint32_t msg, gate_uintptr_t wParam, gate_intptr_t lParam, gate_intptr_t* lresult)
60 {
61 if (ctrl != NULL)
62 {
63 HWND const hwnd = (HWND)ui_hwnd;
64 HWND const hwndCtrl = (HWND)GATE_UI_WINAPI_GET_HWND(ctrl);
65 if (hwnd == hwndCtrl)
66 {
67 gate_ui_paintsurface_t* const surface = (gate_ui_paintsurface_t*)ctrl;
68 gate_ui_point_t coords;
69 gate_uint32_t btn;
70 gate_input_keycode_t kbd_key;
71 gate_input_keystates_t kbd_states;
72
73 switch (msg)
74 {
75 case WM_PAINT:
76 {
77 RECT rect;
78 if (GetUpdateRect(hwndCtrl, &rect, FALSE))
79 {
80 HDC hdc;
81 PAINTSTRUCT ps;
82 RECT client_rect;
83
84 gate_mem_clear(&client_rect, sizeof(client_rect));
85 GetClientRect(hwnd, &client_rect);
86 hdc = BeginPaint(hwnd, &ps);
87 if (hdc)
88 {
89 if (surface->on_paint)
90 {
91 gate_ui_host_t* host = GATE_UI_WINAPI_GET_HOST(ctrl);
92 gate_ui_graphics_t graph;
93 gate_result_t result;
94 gate_ui_position_t position;
95 position.pos.x = rect.left;
96 position.pos.y = rect.top;
97 position.size.width = rect.right - rect.left;
98 position.size.height = rect.bottom - rect.top;
99 if ((position.size.width) > 0 && (position.size.height > 0))
100 {
101 //++position.size.width;
102 //++position.size.height;
103 result = gate_ui_graphics_create_native(&graph, host, (void*)hdc, NULL, client_rect.right, client_rect.bottom);
104 surface->on_paint(ctrl, &graph, &position);
105 }
106 }
107 EndPaint(hwndCtrl, &ps);
108 *lresult = 0;
109 return true;
110 }
111 }
112 break;
113 }
114 case WM_SIZE:
115 {
116 gate_ui_size_t sz;
117 sz.width = (int)(short)LOWORD(lParam);
118 sz.height = (int)(short)HIWORD(lParam);
119 if (surface->on_resize != NULL)
120 {
121 surface->on_resize(ctrl, &sz);
122 }
123 break;
124 }
125 case WM_LBUTTONDOWN:
126 case WM_RBUTTONDOWN:
127 case WM_MBUTTONDOWN:
128 {
129 switch (msg)
130 {
131 case WM_LBUTTONDOWN: btn = GATE_UI_MOUSE_LEFT; break;
132 case WM_RBUTTONDOWN: btn = GATE_UI_MOUSE_RIGHT; break;
133 case WM_MBUTTONDOWN: btn = GATE_UI_MOUSE_MIDDLE; break;
134 }
135 coords.x = GET_X_LPARAM(lParam);
136 coords.y = GET_Y_LPARAM(lParam);
137 if (surface->on_mouse_down != NULL)
138 {
139 surface->on_mouse_down(&surface->ctrl, &coords, btn);
140 *lresult = 0;
141 return true;
142 }
143 break;
144 }
145 case WM_LBUTTONUP:
146 case WM_RBUTTONUP:
147 case WM_MBUTTONUP:
148 {
149 switch (msg)
150 {
151 case WM_LBUTTONUP: btn = GATE_UI_MOUSE_LEFT; break;
152 case WM_RBUTTONUP: btn = GATE_UI_MOUSE_RIGHT; break;
153 case WM_MBUTTONUP: btn = GATE_UI_MOUSE_MIDDLE; break;
154 }
155 coords.x = GET_X_LPARAM(lParam);
156 coords.y = GET_Y_LPARAM(lParam);
157 if (surface->on_mouse_up != NULL)
158 {
159 surface->on_mouse_up(&surface->ctrl, &coords, btn);
160 *lresult = 0;
161 return true;
162 }
163 break;
164 }
165 case WM_LBUTTONDBLCLK:
166 case WM_RBUTTONDBLCLK:
167 case WM_MBUTTONDBLCLK:
168 {
169 switch (msg)
170 {
171 case WM_LBUTTONDBLCLK: btn = GATE_UI_MOUSE_LEFT; break;
172 case WM_RBUTTONDBLCLK: btn = GATE_UI_MOUSE_RIGHT; break;
173 case WM_MBUTTONDBLCLK: btn = GATE_UI_MOUSE_MIDDLE; break;
174 }
175 coords.x = GET_X_LPARAM(lParam);
176 coords.y = GET_Y_LPARAM(lParam);
177 if (surface->on_mouse_dblclick != NULL)
178 {
179 surface->on_mouse_dblclick(&surface->ctrl, &coords, btn);
180 *lresult = 0;
181 return true;
182 }
183 break;
184 }
185 case WM_MOUSEMOVE:
186 {
187 btn = 0;
188 if (GATE_FLAG_ENABLED(LOWORD(wParam), MK_LBUTTON)) btn |= GATE_UI_MOUSE_LEFT;
189 if (GATE_FLAG_ENABLED(LOWORD(wParam), MK_RBUTTON)) btn |= GATE_UI_MOUSE_RIGHT;
190 if (GATE_FLAG_ENABLED(LOWORD(wParam), MK_MBUTTON)) btn |= GATE_UI_MOUSE_MIDDLE;
191 coords.x = GET_X_LPARAM(lParam);
192 coords.y = GET_Y_LPARAM(lParam);
193 if (surface->on_mouse_move != NULL)
194 {
195 surface->on_mouse_move(&surface->ctrl, &coords, btn);
196 *lresult = 0;
197 return true;
198 }
199 break;
200 }
201 case WM_MOUSEWHEEL:
202 {
203 gate_int32_t delta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
204 btn = 0;
205 if (GATE_FLAG_ENABLED(LOWORD(wParam), MK_LBUTTON)) btn |= GATE_UI_MOUSE_LEFT;
206 if (GATE_FLAG_ENABLED(LOWORD(wParam), MK_RBUTTON)) btn |= GATE_UI_MOUSE_RIGHT;
207 if (GATE_FLAG_ENABLED(LOWORD(wParam), MK_MBUTTON)) btn |= GATE_UI_MOUSE_MIDDLE;
208 coords.x = GET_X_LPARAM(lParam);
209 coords.y = GET_Y_LPARAM(lParam);
210 if (surface->on_mouse_scroll != NULL)
211 {
212 surface->on_mouse_scroll(&surface->ctrl, &coords, btn, delta);
213 *lresult = 0;
214 return true;
215 }
216 break;
217 }
218 case WM_KEYDOWN:
219 {
220 if (surface->on_key_down != NULL)
221 {
222 kbd_states = 0;
223 kbd_key = 0;
224 gate_ui_winapi_get_keystates(&kbd_states);
225 gate_keyboard_parse_native_key((gate_input_nativekeycode_t)wParam, &kbd_key, NULL);
226 surface->on_key_down(&surface->ctrl, kbd_key, kbd_states);
227 *lresult = 0;
228 return true;
229 }
230 break;
231 }
232 case WM_KEYUP:
233 {
234 if (surface->on_key_up != NULL)
235 {
236 kbd_states = 0;
237 kbd_key = 0;
238 gate_ui_winapi_get_keystates(&kbd_states);
239 gate_keyboard_parse_native_key((gate_input_nativekeycode_t)wParam, &kbd_key, NULL);
240 surface->on_key_up(&surface->ctrl, kbd_key, kbd_states);
241 *lresult = 0;
242 return true;
243 }
244 break;
245 }
246 case WM_CHAR:
247 {
248 if (surface->on_key_char != NULL)
249 {
250 kbd_states = 0;
251 gate_ui_winapi_get_keystates(&kbd_states);
252 surface->on_key_char(&surface->ctrl, (gate_char32_t)wParam, kbd_states);
253 *lresult = 0;
254 return true;
255 }
256 break;
257 }
258 case WM_SETCURSOR:
259 {
260 HCURSOR const hcur = (HCURSOR)surface->cursor.resources[0];
261 if (hcur != NULL)
262 {
263 SetCursor(hcur);
264 *lresult = TRUE;
265 return true;
266 }
267 break;
268 }
269 }
270 }
271 }
272 return false;
273 }
274
275 gate_result_t gate_ui_paintsurface_create(
276 gate_ui_paintsurface_t* paintsurface, gate_ui_ctrl_t* parent,
277 gate_ui_position_t const* position, gate_uint32_t flags, void* userparam)
278 {
279 ui_hwnd_t const hwndparent = GATE_UI_WINAPI_GET_HWND(parent);
280 gate_ui_host_t* const host = GATE_UI_WINAPI_GET_HOST(parent);
281 gate_result_t ret;
282 gate_uint32_t styles, exstyles;
283 gate_mem_clear(paintsurface, sizeof(gate_ui_paintsurface_t));
284
285 exstyles = 0;
286 styles = WS_CLIPSIBLINGS | WS_CHILD;
287
288 if (!GATE_FLAG_ENABLED(flags, GATE_UI_FLAG_ENABLED)) styles |= WS_DISABLED;
289 if (GATE_FLAG_ENABLED(flags, GATE_UI_FLAG_VISIBLE)) styles |= WS_VISIBLE;
290
291 ret = gate_ui_winapi_create(&paintsurface->ctrl, host, hwndparent, NULL, position, styles, exstyles, NULL, userparam, false);
292 if (GATE_SUCCEEDED(ret))
293 {
294 ui_hwnd_t const hwnd = GATE_UI_WINAPI_GET_HWND(&paintsurface->ctrl);
295 gate_ui_winapi_register_event(host, hwnd, WM_SIZE, &gate_ui_paintsurface_events, &paintsurface->ctrl);
296 gate_ui_winapi_register_event(host, hwnd, WM_PAINT, &gate_ui_paintsurface_events, &paintsurface->ctrl);
297 gate_ui_winapi_register_event(host, hwnd, WM_LBUTTONDOWN, &gate_ui_paintsurface_events, &paintsurface->ctrl);
298 gate_ui_winapi_register_event(host, hwnd, WM_RBUTTONDOWN, &gate_ui_paintsurface_events, &paintsurface->ctrl);
299 gate_ui_winapi_register_event(host, hwnd, WM_MBUTTONDOWN, &gate_ui_paintsurface_events, &paintsurface->ctrl);
300 gate_ui_winapi_register_event(host, hwnd, WM_LBUTTONUP, &gate_ui_paintsurface_events, &paintsurface->ctrl);
301 gate_ui_winapi_register_event(host, hwnd, WM_RBUTTONUP, &gate_ui_paintsurface_events, &paintsurface->ctrl);
302 gate_ui_winapi_register_event(host, hwnd, WM_MBUTTONUP, &gate_ui_paintsurface_events, &paintsurface->ctrl);
303 gate_ui_winapi_register_event(host, hwnd, WM_LBUTTONDBLCLK, &gate_ui_paintsurface_events, &paintsurface->ctrl);
304 gate_ui_winapi_register_event(host, hwnd, WM_RBUTTONDBLCLK, &gate_ui_paintsurface_events, &paintsurface->ctrl);
305 gate_ui_winapi_register_event(host, hwnd, WM_MBUTTONDBLCLK, &gate_ui_paintsurface_events, &paintsurface->ctrl);
306 gate_ui_winapi_register_event(host, hwnd, WM_MOUSEMOVE, &gate_ui_paintsurface_events, &paintsurface->ctrl);
307 gate_ui_winapi_register_event(host, hwnd, WM_MOUSEWHEEL, &gate_ui_paintsurface_events, &paintsurface->ctrl);
308 gate_ui_winapi_register_event(host, hwnd, WM_KEYDOWN, &gate_ui_paintsurface_events, &paintsurface->ctrl);
309 gate_ui_winapi_register_event(host, hwnd, WM_KEYUP, &gate_ui_paintsurface_events, &paintsurface->ctrl);
310 gate_ui_winapi_register_event(host, hwnd, WM_CHAR, &gate_ui_paintsurface_events, &paintsurface->ctrl);
311 gate_ui_winapi_register_event(host, hwnd, WM_SETCURSOR, &gate_ui_paintsurface_events, &paintsurface->ctrl);
312
313 gate_mem_clear(&paintsurface->cursor, sizeof(gate_ui_cursor_t));
314 }
315 return ret;
316 }
317
318 gate_result_t gate_ui_paintsurface_redraw(gate_ui_paintsurface_t* paintsurface)
319 {
320 HWND hwnd = (HWND)GATE_UI_WINAPI_GET_HWND(&paintsurface->ctrl);
321 if (!RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_NOERASE))
322 {
323 return GATE_RESULT_FAILED;
324 }
325 else
326 {
327 return GATE_RESULT_OK;
328 }
329 }
330
331 gate_result_t gate_ui_paintsurface_set_cursor(gate_ui_paintsurface_t* paintsurface, gate_ui_cursor_t const* cursor)
332 {
333 GATE_DEBUG_ASSERT(paintsurface != NULL);
334 if (cursor == NULL)
335 {
336 gate_ui_cursor_destroy(&paintsurface->cursor);
337 return GATE_RESULT_OK;
338 }
339 else
340 {
341 gate_ui_cursor_t new_cursor;
342 gate_result_t const result = gate_ui_cursor_copy(&new_cursor, cursor);
343 if (GATE_SUCCEEDED(result))
344 {
345 gate_ui_cursor_destroy(&paintsurface->cursor);
346 gate_mem_copy(&paintsurface->cursor, &new_cursor, sizeof(gate_ui_cursor_t));
347 }
348 return result;
349 }
350 }
351
352 gate_result_t gate_ui_paintsurface_perform_action(
353 gate_ui_paintsurface_t* paintsurface,
354 gate_enumint_t action,
355 gate_intptr_t param)
356 {
357 gate_result_t ret = GATE_RESULT_NOTSUPPORTED;
358 HWND const hwnd = (HWND)GATE_UI_WINAPI_GET_HWND(&paintsurface->ctrl);
359 gate_win32_userapi_t const* const user = gate_win32_userapi();
360 if (user == NULL)
361 {
362 return GATE_RESULT_NOTSUPPORTED;
363 }
364 switch(action)
365 {
366 case GATE_UI_ACTION_ACTIVATE:
367 {
368 RECT rect;
369 LONG mx, my;
370 user->UserGetWindowRect(hwnd, &rect);
371 mx = rect.left + (rect.right - rect.left) / 2;
372 my = rect.top + (rect.bottom - rect.top) / 2;
373 gate_ui_winapi_move_mouse_global((ui_hwnd_t)hwnd, mx, my);
374 #if defined(GATE_SYS_WIN16)
375 /* TODO */
376 #else
377 user->UserMouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
378 user->UserMouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
379 #endif
380 ret = GATE_RESULT_OK;
381 break;
382 }
383 case GATE_UI_ACTION_CONTEXTMENU:
384 {
385 RECT rect;
386 LONG mx, my;
387 user->UserGetWindowRect(hwnd, &rect);
388 mx = rect.left + (rect.right - rect.left) / 2;
389 my = rect.top + (rect.bottom - rect.top) / 2;
390 gate_ui_winapi_move_mouse_global((ui_hwnd_t)hwnd, mx, my);
391 #if defined(GATE_SYS_WIN16)
392 /* TODO */
393 #else
394 user->UserMouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
395 user->UserMouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
396 #endif
397 ret = GATE_RESULT_OK;
398 break;
399 }
400 case GATE_UI_ACTION_KEYPRESS:
401 {
402 #if defined(GATE_SYS_WIN16)
403 /* TODO */
404 #else
405 user->UserKeybd_event((BYTE)param, 0, 0, 0);
406 user->UserKeybd_event((BYTE)param, KEYEVENTF_KEYUP, 0, 0);
407 #endif
408 ret = GATE_RESULT_OK;
409 break;
410 }
411 }
412 return ret;
413 }
414
415
416
417 #endif /* GATE_UI_WINAPI */
418
419
420 #if defined(GATE_UI_GTK)
421
422 #include "gate/ui/gateui_gtk.h"
423
424 static gate_result_t gate_ui_gtk_paintsurface_refresh(gate_ui_ctrl_t* ctrl)
425 {
426 gate_result_t ret = GATE_RESULT_FAILED;
427 return ret;
428 }
429 static gate_result_t gate_ui_gtk_paintsurface_destroy(gate_ui_ctrl_t* ctrl)
430 {
431 gate_result_t ret = GATE_RESULT_FAILED;
432 return ret;
433 }
434
435 static gboolean gate_ui_gdk_paintsurface_button_press_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
436 {
437 gate_ui_paintsurface_t* paintsurface = (gate_ui_paintsurface_t*)user_data;
438
439 if (paintsurface->on_mouse_down)
440 {
441 gate_ui_point_t point;
442 gate_uint32_t btn = 0;
443 switch (evt->button.button)
444 {
445 case 1: btn = GATE_UI_MOUSE_LEFT; break;
446 case 2: btn = GATE_UI_MOUSE_MIDDLE; break;
447 case 3: btn = GATE_UI_MOUSE_LEFT; break;
448 case 4: btn = GATE_UI_MOUSE_SPECIAL1; break;
449 case 5: btn = GATE_UI_MOUSE_SPECIAL2; break;
450 default: btn = GATE_UI_MOUSE_UNKNOWN; break;
451 }
452 point.x = (gate_int32_t)evt->button.x;
453 point.y = (gate_int32_t)evt->button.y;
454
455 paintsurface->on_mouse_down(&paintsurface->ctrl, &point, btn);
456 }
457 return FALSE;
458 }
459 static gboolean gate_ui_gdk_paintsurface_button_release_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
460 {
461 gate_ui_paintsurface_t* paintsurface = (gate_ui_paintsurface_t*)user_data;
462
463 if (paintsurface->on_mouse_up)
464 {
465 gate_ui_point_t point;
466 gate_uint32_t btn = GATE_UI_MOUSE_UNKNOWN;
467 switch (evt->button.button)
468 {
469 case 1: btn = GATE_UI_MOUSE_LEFT; break;
470 case 2: btn = GATE_UI_MOUSE_MIDDLE; break;
471 case 3: btn = GATE_UI_MOUSE_LEFT; break;
472 case 4: btn = GATE_UI_MOUSE_SPECIAL1; break;
473 case 5: btn = GATE_UI_MOUSE_SPECIAL2; break;
474 }
475 point.x = (gate_int32_t)evt->button.x;
476 point.y = (gate_int32_t)evt->button.y;
477
478 paintsurface->on_mouse_up(&paintsurface->ctrl, &point, btn);
479 }
480 return FALSE;
481 }
482 static gboolean gate_ui_gdk_paintsurface_enter_notify_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
483 {
484 gate_ui_paintsurface_t* const paintsurface = (gate_ui_paintsurface_t*)user_data;
485 GATE_UNUSED_ARG(evt);
486 if (paintsurface != NULL)
487 {
488 if (paintsurface->on_mouse_enter)
489 {
490 paintsurface->on_mouse_enter(&paintsurface->ctrl);
491 }
492
493 {
494 GdkWindow* const window = gtk_widget_get_window(widget);
495 GdkCursor* const cursor = (GdkCursor*)paintsurface->cursor.resources[0];
496 gdk_window_set_cursor(window, cursor);
497 }
498 }
499 return FALSE;
500 }
501 static gboolean gate_ui_gdk_paintsurface_leave_notify_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
502 {
503 gate_ui_paintsurface_t* const paintsurface = (gate_ui_paintsurface_t*)user_data;
504 GATE_UNUSED_ARG(evt);
505 if (paintsurface != NULL)
506 {
507 GdkWindow* const win = gtk_widget_get_window(widget);
508 gdk_window_set_cursor(win, NULL);
509
510 if (paintsurface->on_mouse_leave)
511 {
512 paintsurface->on_mouse_leave(&paintsurface->ctrl);
513 }
514 }
515 return FALSE;
516 }
517
518 static gboolean gate_ui_gdk_paintsurface_focus_in_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
519 {
520 gate_ui_paintsurface_t* paintsurface = (gate_ui_paintsurface_t*)user_data;
521 (void)evt;
522 if (paintsurface->on_focus_changed)
523 {
524 paintsurface->on_focus_changed(&paintsurface->ctrl, true);
525 }
526 return FALSE;
527 }
528 static gboolean gate_ui_gdk_paintsurface_focus_out_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
529 {
530 gate_ui_paintsurface_t* paintsurface = (gate_ui_paintsurface_t*)user_data;
531 if (paintsurface->on_focus_changed)
532 {
533 paintsurface->on_focus_changed(&paintsurface->ctrl, false);
534 }
535 return FALSE;
536 }
537
538 static gboolean gate_ui_gdk_paintsurface_key_press_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
539 {
540 gate_ui_paintsurface_t* paintsurface = (gate_ui_paintsurface_t*)user_data;
541 gate_input_keycode_t key_code = 0;
542 gate_input_keystates_t ctrl_keys = 0;
543 gate_char32_t chr32 = 0;
544
545 if (GATE_FLAG_ENABLED(evt->key.state, GDK_SHIFT_MASK)) ctrl_keys |= GATE_UI_KEY_SHIFT;
546 if (GATE_FLAG_ENABLED(evt->key.state, GDK_CONTROL_MASK)) ctrl_keys |= GATE_UI_KEY_CTRL;
547 if (GATE_FLAG_ENABLED(evt->key.state, GDK_MOD1_MASK)) ctrl_keys |= GATE_UI_KEY_ALT;
548
549 if (paintsurface->on_key_down)
550 {
551 gate_ui_gtk_convert_keyval(evt->key.keyval, &key_code);
552 paintsurface->on_key_down(&paintsurface->ctrl, key_code, ctrl_keys);
553 }
554 if (paintsurface->on_key_char)
555 {
556 chr32 = gdk_keyval_to_unicode(evt->key.keyval);
557 paintsurface->on_key_char(&paintsurface->ctrl, chr32, ctrl_keys);
558 }
559 return FALSE;
560 }
561 static gboolean gate_ui_gdk_paintsurface_key_release_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
562 {
563 gate_ui_paintsurface_t* paintsurface = (gate_ui_paintsurface_t*)user_data;
564 gate_uint32_t key_code = 0;
565 gate_uint32_t ctrl_keys = 0;
566 gate_char32_t chr32 = 0;
567
568 if (GATE_FLAG_ENABLED(evt->key.state, GDK_SHIFT_MASK)) ctrl_keys |= GATE_UI_KEY_SHIFT;
569 if (GATE_FLAG_ENABLED(evt->key.state, GDK_CONTROL_MASK)) ctrl_keys |= GATE_UI_KEY_CTRL;
570 if (GATE_FLAG_ENABLED(evt->key.state, GDK_MOD1_MASK)) ctrl_keys |= GATE_UI_KEY_ALT;
571
572 if (paintsurface->on_key_up)
573 {
574 gate_ui_gtk_convert_keyval(evt->key.keyval, &key_code);
575 paintsurface->on_key_up(&paintsurface->ctrl, key_code, ctrl_keys);
576 }
577 return FALSE;
578 }
579 static gboolean gate_ui_gdk_paintsurface_scroll_event(GtkWidget* widget, GdkEvent* evt, gpointer user_data)
580 {
581 return FALSE;
582 }
583 static void gate_ui_gdk_paintsurface_destroy(GtkWidget* object, gpointer user_data)
584 {
585 gate_ui_paintsurface_t* const paintsurface = (gate_ui_paintsurface_t*)user_data;
586 gate_ui_cursor_destroy(&paintsurface->cursor);
587 }
588
589 static gboolean gate_ui_gdk_paintsurface_draw(GtkWidget* widget, cairo_t* cr, gpointer user_data)
590 {
591 gate_ui_paintsurface_t* paintsurface = (gate_ui_paintsurface_t*)user_data;
592 gate_ui_graphics_t graph = GATE_INIT_EMPTY;
593
594 if (paintsurface && paintsurface->on_paint)
595 {
596 gate_ui_host_t* host = GATE_UI_GTK_GET_CTRL_HOST(&paintsurface->ctrl);
597 GtkWidget* w = GATE_UI_GTK_GET_CTRL_WIDGET(&paintsurface->ctrl);
598 int width = gtk_widget_get_allocated_width(w);
599 int height = gtk_widget_get_allocated_height(w);
600 gate_result_t result = gate_ui_graphics_create_native(&graph, host, cr, NULL, width, height);
601 if (GATE_SUCCEEDED(result))
602 {
603 gate_ui_position_t position;
604 position.pos.x = 0;
605 position.pos.y = 0;
606 position.size.width = width;
607 position.size.height = height,
608 paintsurface->on_paint(&paintsurface->ctrl, &graph, &position);
609 }
610 }
611 return FALSE;
612 }
613
614
615
616 static gate_ui_gtk_dispatcher_t gate_ui_gtk_paintsurface_dispatcher =
617 {
618 NULL,
619 NULL,
620 NULL,
621 NULL,
622 NULL,
623 &gate_ui_gtk_paintsurface_refresh,
624 &gate_ui_gtk_paintsurface_destroy
625 };
626
627 #include "gtk/gtkdrawingarea.h"
628
629 gate_result_t gate_ui_paintsurface_create(
630 gate_ui_paintsurface_t* paintsurface, gate_ui_ctrl_t* parent,
631 gate_ui_position_t const* position, gate_uint32_t flags, void* userparam)
632 {
633 gate_result_t ret = GATE_RESULT_OK;
634 GtkWidget* widget;
635 gate_ui_host_t* host = GATE_UI_GTK_GET_CTRL_HOST(parent);
636
637 gate_mem_clear(paintsurface, sizeof(gate_ui_paintsurface_t));
638 widget = gtk_drawing_area_new();
639 if (widget == NULL)
640 {
641 return GATE_RESULT_FAILED;
642 }
643
644 ret = gate_ui_gtk_ctrl_init(&paintsurface->ctrl, widget, host, userparam, parent,
645 &gate_ui_gtk_paintsurface_dispatcher, false, false, position, &flags);
646
647 if (GATE_SUCCEEDED(ret))
648 {
649 gint desired_events = GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK
650 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
651 | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
652 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
653 | GDK_FOCUS_CHANGE_MASK | GDK_SCROLL_MASK;
654
655 gtk_widget_add_events(widget, desired_events);
656
657 g_signal_connect(G_OBJECT(widget), "button-press-event", G_CALLBACK(gate_ui_gdk_paintsurface_button_press_event), (gpointer)paintsurface);
658 g_signal_connect(G_OBJECT(widget), "button-release-event", G_CALLBACK(gate_ui_gdk_paintsurface_button_release_event), (gpointer)paintsurface);
659 g_signal_connect(G_OBJECT(widget), "scroll-event", G_CALLBACK(gate_ui_gdk_paintsurface_scroll_event), (gpointer)paintsurface);
660 g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(gate_ui_gdk_paintsurface_key_press_event), (gpointer)paintsurface);
661 g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(gate_ui_gdk_paintsurface_key_release_event), (gpointer)paintsurface);
662 g_signal_connect(G_OBJECT(widget), "enter-notify-event", G_CALLBACK(gate_ui_gdk_paintsurface_enter_notify_event), (gpointer)paintsurface);
663 g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(gate_ui_gdk_paintsurface_leave_notify_event), (gpointer)paintsurface);
664 g_signal_connect(G_OBJECT(widget), "focus-in-event", G_CALLBACK(gate_ui_gdk_paintsurface_focus_in_event), (gpointer)paintsurface);
665 g_signal_connect(G_OBJECT(widget), "focus-out-event", G_CALLBACK(gate_ui_gdk_paintsurface_focus_out_event), (gpointer)paintsurface);
666 g_signal_connect(G_OBJECT(widget), "draw", G_CALLBACK(gate_ui_gdk_paintsurface_draw), (gpointer)paintsurface);
667 g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(gate_ui_gdk_paintsurface_destroy), (gpointer)paintsurface);
668 }
669
670 return ret;
671 }
672
673 gate_result_t gate_ui_paintsurface_redraw(gate_ui_paintsurface_t* paintsurface)
674 {
675 gate_result_t ret = gate_ui_gtk_ctrl_refresh(&paintsurface->ctrl);
676 return ret;
677 }
678
679 gate_result_t gate_ui_paintsurface_set_cursor(gate_ui_paintsurface_t* paintsurface, gate_ui_cursor_t const* cursor)
680 {
681 GATE_DEBUG_ASSERT(paintsurface != NULL);
682 if (cursor == NULL)
683 {
684 gate_ui_cursor_destroy(&paintsurface->cursor);
685 return GATE_RESULT_OK;
686 }
687 else
688 {
689 gate_ui_cursor_t new_cursor;
690 gate_result_t const result = gate_ui_cursor_copy(&new_cursor, cursor);
691 if (GATE_SUCCEEDED(result))
692 {
693 gate_ui_cursor_destroy(&paintsurface->cursor);
694 gate_mem_copy(&paintsurface->cursor, &new_cursor, sizeof(new_cursor));
695 }
696 return result;
697 }
698 }
699
700 static void send_gtk_mouse_event(gate_ui_ctrl_t* ctrl, unsigned button_id, int x, int y, gate_bool_t press)
701 {
702 GtkWidget* const widget = GATE_UI_GTK_GET_CTRL_WIDGET(ctrl);
703 GdkWindow* const window = gtk_widget_get_window(widget);
704 GdkDisplay* const dpy = gdk_window_get_display(window);
705 GdkEvent* const evt = gdk_event_new(press ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
706
707 evt->button.window = g_object_ref(window);
708 evt->button.send_event = TRUE;
709 evt->button.time = GDK_CURRENT_TIME;
710 evt->button.x = (gdouble)x;
711 evt->button.y = (gdouble)y;
712 evt->button.x_root = 0.0;
713 evt->button.y_root = 0.0;
714 evt->button.button = button_id;
715 evt->button.state = 0;
716 /* evt->button.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(dpy)); */
717
718 if (press)
719 {
720 gate_ui_gdk_paintsurface_button_press_event(widget, evt, ctrl);
721 }
722 else
723 {
724 gate_ui_gdk_paintsurface_button_release_event(widget, evt, ctrl);
725 }
726
727 gdk_event_free(evt);
728 }
729
730 static void send_gtk_ascii_key(gate_ui_ctrl_t* ctrl, char ascii, gate_bool_t press)
731 {
732 guint const keyval = gdk_unicode_to_keyval((guint32)ascii);
733 if (keyval != GDK_KEY_VoidSymbol)
734 {
735 GtkWidget* const widget = GATE_UI_GTK_GET_CTRL_WIDGET(ctrl);
736 GdkWindow* const window = gtk_widget_get_window(widget);
737 GdkEvent* const evt = gdk_event_new(press ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
738
739 evt->key.window = g_object_ref(window);
740 evt->key.send_event = TRUE;
741 evt->key.time = GDK_CURRENT_TIME;
742
743 evt->key.state = 0;
744 evt->key.keyval = keyval;
745 evt->key.hardware_keycode = 0;
746 evt->key.group = 0;
747 evt->key.is_modifier = FALSE;
748 if (press)
749 {
750 gate_ui_gdk_paintsurface_button_press_event(widget, evt, ctrl);
751 }
752 else
753 {
754 gate_ui_gdk_paintsurface_button_release_event(widget, evt, ctrl);
755 }
756 gdk_event_free(evt);
757 }
758 }
759
760 gate_result_t gate_ui_paintsurface_perform_action(gate_ui_paintsurface_t* paintsurface, gate_enumint_t action, gate_intptr_t param)
761 {
762 gate_result_t ret = GATE_RESULT_NOTSUPPORTED;
763 GtkWidget* const widget = GATE_UI_GTK_GET_CTRL_WIDGET(&paintsurface->ctrl);
764 gate_ui_host_t* const host = GATE_UI_GTK_GET_CTRL_HOST(&paintsurface->ctrl);
765
766 switch(action)
767 {
768 case GATE_UI_ACTION_ACTIVATE:
769 case GATE_UI_ACTION_CONTEXTMENU:
770 {
771 unsigned const button_id = (action == GATE_UI_ACTION_CONTEXTMENU) ? 3 : 1;
772 gate_ui_size_t sz = { 0, 0 };
773 unsigned mouse_x, mouse_y;
774 gate_ui_ctrl_get_size(&paintsurface->ctrl, &sz);
775 mouse_x = (unsigned)(sz.width / 2);
776 mouse_y = (unsigned)(sz.height / 2);
777
778 send_gtk_mouse_event(&paintsurface->ctrl, button_id, mouse_x, mouse_y, true);
779 send_gtk_mouse_event(&paintsurface->ctrl, button_id, mouse_x, mouse_y, false);
780 ret = GATE_RESULT_OK;
781 break;
782 }
783 case GATE_UI_ACTION_KEYPRESS:
784 {
785 char const chr = (char)param;
786 send_gtk_ascii_key(&paintsurface->ctrl, chr, true);
787 send_gtk_ascii_key(&paintsurface->ctrl, chr, false);
788 ret = GATE_RESULT_OK;
789 break;
790 }
791 }
792 return ret;
793 }
794
795 #endif /* GATE_UI_GTK */
796
797
798
799 #if defined(GATE_UI_MOTIF)
800
801 #include "gate/ui/gateui_motif.h"
802 #include <Xm/DrawingA.h>
803
804 2 static void motif_paintsurface_on_expose(gate_ui_paintsurface_t* surface, gate_ui_position_t const* pos)
805 {
806 gate_result_t result;
807 2 gate_ui_graphics_t graph = GATE_INIT_EMPTY;
808
809 2 result = gate_ui_graphics_create_ctrl(&graph, &surface->ctrl);
810
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (GATE_SUCCEEDED(result))
811 {
812 2 surface->on_paint(&surface->ctrl, &graph, pos);
813 2 gate_ui_graphics_destroy(&graph);
814 }
815 2 }
816
817 7 static void drawing_area_callback(Widget widget, XtPointer client_data, XtPointer call_data)
818 {
819 7 XmDrawingAreaCallbackStruct* cbs = (XmDrawingAreaCallbackStruct*)call_data;
820 7 gate_ui_paintsurface_t* surface = (gate_ui_paintsurface_t*)client_data;
821 XEvent* xevt;
822 Display* dpy;
823 7 gate_ui_graphics_t graph = GATE_INIT_EMPTY;
824 7 gate_ui_position_t pos = GATE_INIT_EMPTY;
825 7 Widget w = NULL;
826 gate_result_t result;
827 gate_enumint_t mousebtn;
828
829
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if (!cbs || !surface)
830 {
831 return;
832 }
833
834 7 xevt = cbs->event;
835
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!xevt)
836 {
837 return;
838 }
839 7 dpy = xevt->xany.display;
840
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!dpy)
841 {
842 return;
843 }
844 7 w = GATE_UI_MOTIF_GET_CTRL_WIDGET(&surface->ctrl);
845
846
2/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7 switch (cbs->reason)
847 {
848 6 case XmCR_INPUT:
849 {
850
4/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
6 switch (xevt->xany.type)
851 {
852 2 case ButtonPress:
853 {
854 2 pos.pos.x = xevt->xbutton.x;
855 2 pos.pos.y = xevt->xbutton.y;
856 2 mousebtn = GATE_UI_MOUSE_UNKNOWN;
857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (xevt->xbutton.button & Button1Mask) mousebtn = GATE_UI_MOUSE_LEFT;
858
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (xevt->xbutton.button & Button2Mask) mousebtn = GATE_UI_MOUSE_MIDDLE;
859
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (xevt->xbutton.button & Button3Mask) mousebtn = GATE_UI_MOUSE_RIGHT;
860 2 surface->on_mouse_down(&surface->ctrl, &pos.pos, mousebtn);
861 2 break;
862 }
863 2 case ButtonRelease:
864 {
865 2 pos.pos.x = xevt->xbutton.x;
866 2 pos.pos.y = xevt->xbutton.y;
867 2 mousebtn = GATE_UI_MOUSE_UNKNOWN;
868
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (xevt->xbutton.button & Button1Mask) mousebtn = GATE_UI_MOUSE_LEFT;
869
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (xevt->xbutton.button & Button2Mask) mousebtn = GATE_UI_MOUSE_MIDDLE;
870
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (xevt->xbutton.button & Button3Mask) mousebtn = GATE_UI_MOUSE_RIGHT;
871 2 surface->on_mouse_up(&surface->ctrl, &pos.pos, mousebtn);
872 2 break;
873 }
874 case MotionNotify:
875 {
876 pos.pos.x = xevt->xmotion.x;
877 pos.pos.y = xevt->xmotion.y;
878 mousebtn = 0;
879 if (xevt->xmotion.state & Button1Mask) mousebtn |= GATE_UI_MOUSE_LEFT;
880 if (xevt->xmotion.state & Button2Mask) mousebtn |= GATE_UI_MOUSE_MIDDLE;
881 if (xevt->xmotion.state & Button3Mask) mousebtn |= GATE_UI_MOUSE_RIGHT;
882 surface->on_mouse_move(&surface->ctrl, &pos.pos, mousebtn);
883 break;
884 }
885 case EnterNotify:
886 {
887 surface->on_mouse_enter(&surface->ctrl);
888 break;
889 }
890 case LeaveNotify:
891 {
892 surface->on_mouse_leave(&surface->ctrl);
893 break;
894 }
895 1 case KeyPress:
896 {
897 1 surface->on_key_down(&surface->ctrl, xevt->xkey.keycode, 0);
898 1 break;
899 }
900 1 case KeyRelease:
901 {
902 1 surface->on_key_up(&surface->ctrl, xevt->xkey.keycode, 0);
903 1 break;
904 }
905 }
906 6 break;
907 }
908 1 case XmCR_EXPOSE:
909 {
910 1 pos.pos.x = xevt->xexpose.x;
911 1 pos.pos.y = xevt->xexpose.y;
912 1 pos.size.width = xevt->xexpose.width;
913 1 pos.size.height = xevt->xexpose.height;
914 1 motif_paintsurface_on_expose(surface, &pos);
915 1 break;
916 }
917 case XmCR_ACTIVATE:
918 {
919 break;
920 }
921 }
922 7 }
923
924
925 1 gate_result_t gate_ui_paintsurface_create(gate_ui_paintsurface_t* paintsurface, gate_ui_ctrl_t* parent, gate_ui_position_t const* position,
926 gate_uint32_t flags, void* userparam)
927 {
928 1 gate_result_t ret = GATE_RESULT_FAILED;
929
930 do
931 {
932 1 Widget w = NULL;
933 Arg args[8];
934 1 unsigned args_count = 0;
935
936
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!paintsurface || !parent)
937 {
938 ret = GATE_RESULT_INVALIDARG;
939 break;
940 }
941
942 1 XtSetArg(args[args_count], XmNrecomputeSize, 0); ++args_count;
943 1 XtSetArg(args[args_count], XmNshadowThickness, 1); ++args_count;
944 1 XtSetArg(args[args_count], XmNhighlightThickness, 1); ++args_count;
945
946 1 ret = gate_ui_motif_ctrl_create(&paintsurface->ctrl, xmDrawingAreaWidgetClass, userparam, NULL, parent,
947 position, &flags, false, NULL, args, args_count);
948
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
949
950 1 w = GATE_UI_MOTIF_GET_CTRL_WIDGET(&paintsurface->ctrl);
951 1 XtAddCallback(w, XmNinputCallback, &drawing_area_callback, paintsurface);
952 1 XtAddCallback(w, XmNresizeCallback, &drawing_area_callback, paintsurface);
953 1 XtAddCallback(w, XmNexposeCallback, &drawing_area_callback, paintsurface);
954 } while (0);
955
956 1 return ret;
957 }
958
959 1 gate_result_t gate_ui_paintsurface_redraw(gate_ui_paintsurface_t* paintsurface)
960 {
961 Widget w;
962 XExposeEvent xevt;
963 1 gate_ui_host_t* host = NULL;
964 gate_ui_position_t pos;
965 Display* dpy;
966
967
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_DEBUG_ASSERT(paintsurface != NULL);
968 1 w = GATE_UI_MOTIF_GET_CTRL_WIDGET(&paintsurface->ctrl);
969
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_DEBUG_ASSERT(w != NULL);
970 1 host = GATE_UI_MOTIF_GET_CTRL_HOST(&paintsurface->ctrl);
971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_DEBUG_ASSERT(host != NULL);
972 1 dpy = GATE_UI_MOTIF_GET_HOST_DISPLAY(host);
973
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_DEBUG_ASSERT(dpy != NULL);
974
975 1 gate_ui_ctrl_get_position(&paintsurface->ctrl, &pos);
976 1 motif_paintsurface_on_expose(paintsurface, &pos);
977
978 /*
979 xevt.type = Expose;
980 xevt.display = dpy;
981 xevt.window = XtWindow(w);
982 xevt.x = 0;
983 xevt.y = 0;
984 xevt.width = pos.size.width;
985 xevt.height = pos.size.height;
986 xevt.count = 0;
987
988 XSendEvent(dpy, xevt.window, False, ExposureMask, (XEvent*)&xevt);
989 XFlush(dpy);
990 */
991 1 return GATE_RESULT_OK;
992 }
993
994 1 gate_result_t gate_ui_paintsurface_set_cursor(gate_ui_paintsurface_t* paintsurface, gate_ui_cursor_t const* cursor)
995 {
996
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_DEBUG_ASSERT(paintsurface != NULL);
997
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (cursor == NULL)
998 {
999 gate_ui_cursor_destroy(&paintsurface->cursor);
1000 return GATE_RESULT_OK;
1001 }
1002 else
1003 {
1004 gate_ui_cursor_t new_cur;
1005 1 gate_result_t const result = gate_ui_cursor_copy(&new_cur, cursor);
1006
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(result))
1007 {
1008 1 Widget const w = GATE_UI_MOTIF_GET_CTRL_WIDGET(&paintsurface->ctrl);
1009 1 Cursor const cur = (Cursor)(gate_uintptr_t)new_cur.resources[0];
1010 1 XtVaSetValues(w, XmNcursor, cur, NULL);
1011 1 gate_ui_cursor_destroy(&paintsurface->cursor);
1012 1 gate_mem_copy(&paintsurface->cursor, &new_cur, sizeof(gate_ui_cursor_t));
1013 }
1014 1 return result;
1015 }
1016 }
1017
1018 4 static void send_x11_mouse_click(Display* const dpy, Window x11win, int ctrl_mouse_x, int ctrl_mouse_y, int button_id, gate_bool_t press)
1019 {
1020 XButtonEvent ev;
1021 4 int screen_x = 0, screen_y = 0;
1022 4 Window dummy = 0;
1023
1024 4 XTranslateCoordinates(dpy, x11win, DefaultRootWindow(dpy), ctrl_mouse_x, ctrl_mouse_y, &screen_x, &screen_y, &dummy);
1025
1026 4 memset(&ev, 0, sizeof(ev));
1027
1028
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 ev.type = press ? ButtonPress : ButtonRelease;
1029 4 ev.display = dpy;
1030 4 ev.window = x11win;
1031 4 ev.root = DefaultRootWindow(dpy);
1032 4 ev.subwindow= None;
1033 4 ev.time = CurrentTime;
1034
1035 4 ev.x = ctrl_mouse_x;
1036 4 ev.y = ctrl_mouse_y;
1037 4 ev.x_root = screen_x;
1038 4 ev.y_root = screen_y;
1039
1040 4 ev.state = 0;
1041 4 ev.button = button_id;
1042 4 ev.same_screen = True;
1043
1044 4 XSendEvent(dpy, x11win, True, ButtonPressMask | ButtonReleaseMask, (XEvent *)&ev);
1045 4 XFlush(dpy);
1046 4 }
1047
1048 2 static void send_x11_key_press(Display* const dpy, Window x11win, char ascii, gate_bool_t press)
1049 {
1050 2 char const chr[2] = { ascii, 0 };
1051 2 KeySym const key_sym = XStringToKeysym(chr);
1052 2 KeyCode const key_code = XKeysymToKeycode(dpy, key_sym);
1053 2 XKeyEvent event = GATE_INIT_EMPTY;
1054
1055
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 event.type = press ? KeyPress : KeyRelease;
1056 2 event.display = dpy;
1057 2 event.window = x11win;
1058 2 event.root = DefaultRootWindow(dpy);
1059 2 event.subwindow = None;
1060 2 event.time = CurrentTime;
1061 2 event.x = 0;
1062 2 event.y = 0;
1063 2 event.x_root = 0;
1064 2 event.y_root = 0;
1065 2 event.state = 0;
1066 2 event.keycode = key_code;
1067 2 event.same_screen = True;
1068 2 XSendEvent(dpy, x11win, True, KeyPressMask | KeyReleaseMask, (XEvent *)&event);
1069 2 XFlush(dpy);
1070 2 }
1071
1072
1073 3 gate_result_t gate_ui_paintsurface_perform_action(gate_ui_paintsurface_t* paintsurface, gate_enumint_t action, gate_intptr_t param)
1074 {
1075 3 gate_result_t ret = GATE_RESULT_NOTSUPPORTED;
1076 3 Widget const w = GATE_UI_MOTIF_GET_CTRL_WIDGET(&paintsurface->ctrl);
1077 3 gate_ui_host_t* const host = GATE_UI_MOTIF_GET_CTRL_HOST(&paintsurface->ctrl);
1078 3 Display* const dpy = GATE_UI_MOTIF_GET_HOST_DISPLAY(host);
1079 3 Window const x11win = XtWindow(w);
1080
1081
2/3
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
3 switch(action)
1082 {
1083 2 case GATE_UI_ACTION_ACTIVATE:
1084 case GATE_UI_ACTION_CONTEXTMENU:
1085 {
1086
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 int const button_id = (action == GATE_UI_ACTION_CONTEXTMENU) ? Button3 : Button1;
1087 2 gate_ui_size_t sz = { 0, 0 };
1088 int mouse_x, mouse_y;
1089 2 gate_ui_ctrl_get_size(&paintsurface->ctrl, &sz);
1090 2 mouse_x = sz.width / 2;
1091 2 mouse_y = sz.height / 2;
1092
1093 2 send_x11_mouse_click(dpy, x11win, mouse_x, mouse_y, button_id, true);
1094 2 send_x11_mouse_click(dpy, x11win, mouse_x, mouse_y, button_id, false);
1095
1096 2 ret = GATE_RESULT_OK;
1097 2 break;
1098 }
1099 1 case GATE_UI_ACTION_KEYPRESS:
1100 {
1101 1 send_x11_key_press(dpy, x11win, (char)param, true);
1102 1 send_x11_key_press(dpy, x11win, (char)param, false);
1103 1 ret = GATE_RESULT_OK;
1104 1 break;
1105 }
1106 }
1107 3 return ret;
1108 }
1109
1110 #endif /* GATE_UI_MOTIF */
1111