1 /* Copyright (C) 2016 by Jeremy Tan */
2 /*
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
14 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
16 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
19 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
21 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25 #include <fontforge-config.h>
26
27 /**
28 * \file ggdkdraw.c
29 * \brief GDK drawing backend.
30 */
31
32 #include "ggdkdrawP.h"
33
34 #ifdef FONTFORGE_CAN_USE_GDK
35
36 #include "gkeysym.h"
37 #include "gresource.h"
38 #include "ustring.h"
39
40 #include <assert.h>
41 #include <math.h>
42
43 // HACK HACK HACK
44 #ifdef GDK_WINDOWING_WIN32
45 # define GDK_COMPILATION
46 # include <gdk/gdkwin32.h>
47 # undef GDK_COMPILATION
48 #endif
49
50 // Forward declarations
51 static void GGDKDrawCancelTimer(GTimer *timer);
52 static void GGDKDrawDestroyWindow(GWindow w);
53 static void GGDKDrawPostEvent(GEvent *e);
54 static void GGDKDrawProcessPendingEvents(GDisplay *gdisp);
55 static void GGDKDrawSetCursor(GWindow w, GCursor gcursor);
56 static void GGDKDrawSetTransientFor(GWindow transient, GWindow owner);
57 static void GGDKDrawSetWindowBackground(GWindow w, Color gcol);
58
59 // Private member functions (file-level)
60 static void _GGDKDraw_InitiateWindowDestroy(GGDKWindow gw);
61
_GGDKDraw_NewGGC(void)62 static GGC *_GGDKDraw_NewGGC(void) {
63 GGC *ggc = calloc(1, sizeof(GGC));
64 if (ggc == NULL) {
65 Log(LOGDEBUG, "GGC: Memory allocation failed!");
66 return NULL;
67 }
68
69 ggc->clip.width = ggc->clip.height = 0x7fff;
70 ggc->fg = 0;
71 ggc->bg = 0xffffff;
72 return ggc;
73 }
74
_GGDKDraw_SetOpaqueRegion(GGDKWindow gw)75 static void _GGDKDraw_SetOpaqueRegion(GGDKWindow gw) {
76 cairo_rectangle_int_t r = {
77 gw->pos.x, gw->pos.y, gw->pos.width, gw->pos.height
78 };
79 cairo_region_t *cr = cairo_region_create_rectangle(&r);
80 if (cairo_region_status(cr) == CAIRO_STATUS_SUCCESS) {
81 gdk_window_set_opaque_region(gw->w, cr);
82 cairo_region_destroy(cr);
83 }
84 }
85
_GGDKDraw_ClearSelData(GGDKDisplay * gdisp,enum selnames sn)86 static void _GGDKDraw_ClearSelData(GGDKDisplay *gdisp, enum selnames sn) {
87 GList_Glib *ptr = gdisp->selinfo[sn].datalist;
88 while (ptr != NULL) {
89 GGDKSelectionData *data = (GGDKSelectionData *)ptr->data;
90 if (data->data) {
91 if (data->freedata) {
92 (data->freedata)(data->data);
93 } else {
94 free(data->data);
95 }
96 }
97 free(data);
98 ptr = g_list_delete_link(ptr, ptr);
99 }
100 gdisp->selinfo[sn].datalist = NULL;
101 gdisp->selinfo[sn].owner = NULL;
102 }
103
_GGDKDraw_TransmitSelection(GGDKDisplay * gdisp,GdkEventSelection * e)104 static bool _GGDKDraw_TransmitSelection(GGDKDisplay *gdisp, GdkEventSelection *e) {
105 // Default location to store data if none specified
106 if (e->property == GDK_NONE) {
107 e->property = e->target;
108 }
109
110 // Check that we own the selection request
111 enum selnames sn;
112 for (sn = 0; sn < sn_max; sn++) {
113 if (e->selection == gdisp->selinfo[sn].sel_atom) {
114 break;
115 }
116 }
117 if (sn == sn_max) {
118 return false;
119 }
120
121 GGDKSelectionInfo *sel = &gdisp->selinfo[sn];
122 GdkWindow *requestor = (GdkWindow *)g_object_ref(e->requestor);
123
124 if (e->target == gdk_atom_intern_static_string("TARGETS")) {
125 guint i = 0, dlen = g_list_length(sel->datalist);
126 GdkAtom *targets = calloc(2 + dlen, sizeof(GdkAtom));
127 GList_Glib *ptr = sel->datalist;
128
129 targets[i++] = gdk_atom_intern_static_string("TIMESTAMP");
130 targets[i++] = gdk_atom_intern_static_string("TARGETS");
131
132 while (ptr != NULL) {
133 targets[i++] = ((GGDKSelectionData *)ptr->data)->type_atom;
134 ptr = ptr->next;
135 }
136
137 #ifdef GDK_WINDOWING_WIN32
138 gdk_win32_selection_clear_targets(gdk_window_get_display(e->window), e->selection);
139 gdk_win32_selection_add_targets(e->window, e->selection, i, targets);
140 #endif
141
142 gdk_property_change(requestor, e->property, gdk_atom_intern_static_string("ATOM"),
143 32, GDK_PROP_MODE_REPLACE, (const guchar *)targets, i);
144 } else if (e->target == gdk_atom_intern_static_string("TIMESTAMP")) {
145 gdk_property_change(requestor, e->property, gdk_atom_intern_static_string("INTEGER"),
146 32, GDK_PROP_MODE_REPLACE, (const guchar *)&sel->timestamp, 1);
147 } else {
148 GList_Glib *ptr = sel->datalist;
149 GGDKSelectionData *data = NULL;
150 while (ptr != NULL) {
151 if (((GGDKSelectionData *)ptr->data)->type_atom == e->target) {
152 data = (GGDKSelectionData *)ptr->data;
153 break;
154 }
155 ptr = ptr->next;
156 }
157 if (data == NULL) { // Unknown selection
158 g_object_unref(requestor);
159 return false;
160 }
161
162 void *tmp = data->data;
163 int len = data->cnt;
164 if (data->gendata) {
165 tmp = data->gendata(data->data, &len);
166 if (tmp == NULL) {
167 g_object_unref(requestor);
168 return false;
169 }
170 }
171
172 gdk_property_change(requestor, e->property, e->target,
173 data->unit_size * 8, GDK_PROP_MODE_REPLACE, (const guchar *)tmp, len);
174 if (data->gendata) {
175 free(tmp);
176 }
177 }
178
179 g_object_unref(requestor);
180 return true;
181 }
182
_GGDKDraw_OnWindowDestroyed(gpointer data)183 static gboolean _GGDKDraw_OnWindowDestroyed(gpointer data) {
184 GGDKWindow gw = (GGDKWindow)data;
185 Log(LOGDEBUG, "Window: %p", gw);
186 if (gw->is_cleaning_up) {
187 return false;
188 }
189 gw->is_cleaning_up = true; // We're in the process of destroying it.
190
191 if (gw->cc != NULL) {
192 cairo_destroy(gw->cc);
193 gw->cc = NULL;
194 }
195
196 if (gw->resize_timeout != 0) {
197 g_source_remove(gw->resize_timeout);
198 gw->resize_timeout = 0;
199 }
200
201 if (!gw->is_pixmap) {
202 if (gw != gw->display->groot) {
203 if (!gdk_window_is_destroyed(gw->w)) {
204 gdk_window_destroy(gw->w);
205 // Wait for it to die
206 while (!gw->display->is_dying && !gdk_window_is_destroyed(gw->w)) {
207 GGDKDrawProcessPendingEvents((GDisplay *)gw->display);
208 }
209 }
210 }
211
212 // Signal that it has been destroyed - only if we're not cleaning up the display
213 if (!gw->display->is_dying) {
214 struct gevent die = {0};
215 die.w = (GWindow)gw;
216 die.native_window = gw->w;
217 die.type = et_destroy;
218 GGDKDrawPostEvent(&die);
219 }
220
221 // Remove all relevant timers that haven't been cleaned up by the user
222 // Note: We do not free the GTimer struct as the user may then call DestroyTimer themselves...
223 GList_Glib *ent = gw->display->timers;
224 while (ent != NULL) {
225 GList_Glib *next = ent->next;
226 GGDKTimer *timer = (GGDKTimer *)ent->data;
227 if (timer->owner == (GWindow)gw) {
228 //Since we update the timer list ourselves, don't all GGDKDrawCancelTimer.
229 Log(LOGDEBUG, "WARNING: Unstopped timer on window destroy!!! %p -> %p", gw, timer);
230 timer->active = false;
231 timer->stopped = true;
232 g_source_remove(timer->glib_timeout_id);
233 gw->display->timers = g_list_delete_link(gw->display->timers, ent);
234 }
235 ent = next;
236 }
237
238 // If it owns any of the selections, clear it
239 for (int i = 0; i < sn_max; i++) {
240 if (gw->display->selinfo[i].owner == gw) {
241 _GGDKDraw_ClearSelData(gw->display, i);
242 }
243 }
244
245 // Decrement the toplevel window count
246 if (gw->display->groot == gw->parent && !gw->is_dlg) {
247 gw->display->top_window_count--;
248 }
249
250 assert(gw->display->dirty_window != gw);
251
252 Log(LOGDEBUG, "Window destroyed: %p[%p][%s][toplevel:%d][pixmap:%d]",
253 gw, gw->w, gw->window_title, gw->is_toplevel, gw->is_pixmap);
254 free(gw->window_title);
255 if (gw != gw->display->groot) {
256 // Unreference our reference to the window
257 g_object_unref(G_OBJECT(gw->w));
258 }
259 }
260
261 if (gw->pango_layout != NULL) {
262 g_object_unref(gw->pango_layout);
263 }
264
265 if (gw->cs != NULL) {
266 cairo_surface_destroy(gw->cs);
267 }
268
269 g_hash_table_remove(gw->display->windows, gw);
270
271 // Remove our reference on the parent
272 if (gw->parent != NULL && gw->parent != gw->display->groot) {
273 GGDKDRAW_DECREF(gw->parent, _GGDKDraw_InitiateWindowDestroy);
274 }
275
276 free(gw->ggc);
277 free(gw);
278 return false;
279 }
280
281 // FF expects the destroy call to happen asynchronously to
282 // the actual GGDKDrawDestroyWindow call. So we add it to the queue...
_GGDKDraw_InitiateWindowDestroy(GGDKWindow gw)283 static void _GGDKDraw_InitiateWindowDestroy(GGDKWindow gw) {
284 if (gw->is_pixmap) {
285 _GGDKDraw_OnWindowDestroyed(gw);
286 } else if (!gw->is_cleaning_up) { // Check for nested call - if we're already being destroyed.
287 // Note: This *MUST* be a 0-length timer so it always gets picked up on the next
288 // call to GDrawProcessPendingEvents. There are assumptions made that the destroy
289 // event will be invoked when that function is called after calling GDrawDestroyWindow.
290 // Ideally fix wherever the GDrawSync/GDrawProcessPendingEvents pattern is used
291 // to have a more concrete check that it's actually gone.
292 g_timeout_add(0, _GGDKDraw_OnWindowDestroyed, gw);
293 }
294 }
295
_GGDKDraw_OnTimerDestroyed(GGDKTimer * timer)296 static void _GGDKDraw_OnTimerDestroyed(GGDKTimer *timer) {
297 GGDKDisplay *gdisp = ((GGDKWindow)(timer->owner))->display;
298
299 // We may stop the timer without destroying it -
300 // e.g. if the user doesn't cancel the timer before a window is destroyed.
301 if (!timer->stopped) {
302 g_source_remove(timer->glib_timeout_id);
303 }
304 gdisp->timers = g_list_remove(gdisp->timers, timer);
305 free(timer);
306 }
307
_GGDKDraw_OnFakedConfigure(gpointer user_data)308 static gboolean _GGDKDraw_OnFakedConfigure(gpointer user_data) {
309 GGDKWindow gw = (GGDKWindow)user_data;
310 if (!gw->is_dying) {
311 GdkEventConfigure evt = {0};
312
313 evt.type = GDK_CONFIGURE;
314 evt.window = gw->w;
315 evt.send_event = true;
316 evt.width = gdk_window_get_width(gw->w);
317 evt.height = gdk_window_get_height(gw->w);
318 gdk_window_get_position(gw->w, &evt.x, &evt.y);
319
320 gdk_event_put((GdkEvent *)&evt);
321 }
322 gw->resize_timeout = 0;
323 return false;
324 }
325
326 // In their infinite wisdom, GDK does not send configure events for child windows.
_GGDKDraw_FakeConfigureEvent(GGDKWindow gw)327 static void _GGDKDraw_FakeConfigureEvent(GGDKWindow gw) {
328 if (gw->resize_timeout != 0) {
329 g_source_remove(gw->resize_timeout);
330 gw->resize_timeout = 0;
331 }
332
333 // Always fire the first faked configure event immediately
334 if (!gw->has_had_faked_configure) {
335 _GGDKDraw_OnFakedConfigure(gw);
336 gw->has_had_faked_configure = true;
337 } else {
338 gw->resize_timeout = g_timeout_add(150, _GGDKDraw_OnFakedConfigure, gw);
339 }
340 }
341
_GGDKDraw_CallEHChecked(GGDKWindow gw,GEvent * event,int (* eh)(GWindow gw,GEvent *))342 static void _GGDKDraw_CallEHChecked(GGDKWindow gw, GEvent *event, int (*eh)(GWindow gw, GEvent *)) {
343 if (eh) {
344 // Increment reference counter
345 GGDKDRAW_ADDREF(gw);
346 (eh)((GWindow)gw, event);
347
348 // Cleanup after our user...
349 _GGDKDraw_CleanupAutoPaint(gw->display);
350 // Decrement reference counter
351 GGDKDRAW_DECREF(gw, _GGDKDraw_InitiateWindowDestroy);
352 }
353 }
354
_GGDKDraw_GetPointer(GGDKDisplay * gdisp)355 static GdkDevice *_GGDKDraw_GetPointer(GGDKDisplay *gdisp) {
356 #ifdef GGDKDRAW_GDK_3_20
357 GdkSeat *seat = gdk_display_get_default_seat(gdisp->display);
358 if (seat == NULL) {
359 return NULL;
360 }
361
362 return gdk_seat_get_pointer(seat);
363 #else
364 GdkDeviceManager *manager = gdk_display_get_device_manager(gdisp->display);
365 if (manager == NULL) {
366 return NULL;
367 }
368
369 return gdk_device_manager_get_client_pointer(manager);
370 #endif
371 }
372
_GGDKDraw_CenterWindowOnScreen(GGDKWindow gw)373 static void _GGDKDraw_CenterWindowOnScreen(GGDKWindow gw) {
374 GGDKDisplay *gdisp = gw->display;
375 GdkRectangle work_area, window_size;
376 int x, y;
377
378 #ifdef GGDKDRAW_GDK_3_22
379 GdkMonitor *monitor;
380
381 gdk_device_get_position(_GGDKDraw_GetPointer(gdisp), NULL, &x, &y);
382 monitor = gdk_display_get_monitor_at_point(gdisp->display, x, y);
383 gdk_monitor_get_workarea(monitor, &work_area);
384 #else
385 GdkScreen *pointer_screen;
386 int monitor = 0;
387
388 gdk_device_get_position(_GGDKDraw_GetPointer(gdisp), &pointer_screen, &x, &y);
389
390 if (pointer_screen == gdisp->screen) { // Ensure it's on the same screen
391 monitor = gdk_screen_get_monitor_at_point(gdisp->screen, x, y);
392 }
393
394 gdk_screen_get_monitor_workarea(pointer_screen, monitor, &work_area);
395 #endif // GGDKDRAW_GDK_3_22
396
397 gdk_window_get_frame_extents(gw->w, &window_size);
398 gw->pos.x = (work_area.width - window_size.width) / 2 + work_area.x;
399 gw->pos.y = (work_area.height - window_size.height) / 2 + work_area.y;
400
401 if (gw->pos.x < work_area.x) {
402 gw->pos.x = work_area.x;
403 }
404
405 if (gw->pos.y < work_area.y) {
406 gw->pos.y = work_area.y;
407 }
408 gdk_window_move(gw->w, gw->pos.x, gw->pos.y);
409 }
410
_GGDKDraw_CreateWindow(GGDKDisplay * gdisp,GGDKWindow gw,GRect * pos,int (* eh)(GWindow,GEvent *),void * user_data,GWindowAttrs * wattrs)411 static GWindow _GGDKDraw_CreateWindow(GGDKDisplay *gdisp, GGDKWindow gw, GRect *pos,
412 int (*eh)(GWindow, GEvent *), void *user_data, GWindowAttrs *wattrs) {
413
414 GWindowAttrs temp = GWINDOWATTRS_EMPTY;
415 GdkWindowAttr attribs = {0};
416 gint attribs_mask = 0;
417 GGDKWindow nw = (GGDKWindow)calloc(1, sizeof(struct ggdkwindow));
418
419 if (nw == NULL) {
420 Log(LOGDEBUG, "_GGDKDraw_CreateWindow: GGDKWindow calloc failed.");
421 return NULL;
422 }
423 if (wattrs == NULL) {
424 wattrs = &temp;
425 }
426
427 if (gw == NULL) { // Creating a top-level window. Set parent as default root.
428 gw = gdisp->groot;
429 attribs.window_type = GDK_WINDOW_TOPLEVEL;
430 } else {
431 attribs.window_type = GDK_WINDOW_CHILD;
432 }
433
434 // Now check window type
435 if ((wattrs->mask & wam_nodecor) && wattrs->nodecoration) {
436 // Is a modeless dialogue
437 nw->is_popup = true;
438 nw->is_dlg = true;
439 nw->not_restricted = true;
440 attribs.window_type = GDK_WINDOW_TEMP;
441 } else if ((wattrs->mask & wam_isdlg) && wattrs->is_dlg) {
442 nw->is_dlg = true;
443 }
444 if ((wattrs->mask & wam_notrestricted) && wattrs->not_restricted) {
445 nw->not_restricted = true;
446 }
447 nw->is_toplevel = attribs.window_type != GDK_WINDOW_CHILD;
448
449 // Drawing context
450 nw->ggc = _GGDKDraw_NewGGC();
451 if (nw->ggc == NULL) {
452 Log(LOGDEBUG, "_GGDKDraw_CreateWindow: _GGDKDraw_NewGGC returned NULL");
453 free(nw);
454 return NULL;
455 }
456
457 // Base fields
458 nw->display = gdisp;
459 nw->eh = eh;
460 nw->parent = gw;
461 nw->pos = *pos;
462 nw->user_data = user_data;
463
464 // Window title, hints and event mask
465 attribs.event_mask = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK;
466 if (nw->is_toplevel) {
467 // Default event mask for toplevel windows
468 attribs.event_mask |= GDK_FOCUS_CHANGE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
469
470 // Icon titles are ignored.
471 if ((wattrs->mask & wam_utf8_wtitle) && (wattrs->utf8_window_title != NULL)) {
472 nw->window_title = copy(wattrs->utf8_window_title);
473 } else if ((wattrs->mask & wam_wtitle) && (wattrs->window_title != NULL)) {
474 nw->window_title = u2utf8_copy(wattrs->window_title);
475 }
476
477 attribs.title = nw->window_title;
478 attribs.type_hint = (nw->is_popup || (wattrs->mask & wam_palette)) ?
479 GDK_WINDOW_TYPE_HINT_UTILITY : GDK_WINDOW_TYPE_HINT_NORMAL;
480
481 if (attribs.title != NULL) {
482 attribs_mask |= GDK_WA_TITLE;
483 }
484 attribs_mask |= GDK_WA_TYPE_HINT;
485 }
486
487 // Further event mask flags
488 if (wattrs->mask & wam_events) {
489 if (wattrs->event_masks & (1 << et_char)) {
490 attribs.event_mask |= GDK_KEY_PRESS_MASK;
491 }
492 if (wattrs->event_masks & (1 << et_charup)) {
493 attribs.event_mask |= GDK_KEY_RELEASE_MASK;
494 }
495 if (wattrs->event_masks & (1 << et_mousemove)) {
496 attribs.event_mask |= GDK_POINTER_MOTION_MASK;
497 }
498 if (wattrs->event_masks & (1 << et_mousedown)) {
499 attribs.event_mask |= GDK_BUTTON_PRESS_MASK | GDK_SCROLL_MASK;
500 }
501 if (wattrs->event_masks & (1 << et_mouseup)) {
502 attribs.event_mask |= GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK;
503 }
504 if (wattrs->event_masks & (1 << et_visibility)) {
505 attribs.event_mask |= GDK_VISIBILITY_NOTIFY_MASK;
506 }
507 }
508
509 if (wattrs->mask & wam_restrict) {
510 nw->restrict_input_to_me = wattrs->restrict_input_to_me;
511 }
512 if (wattrs->mask & wam_redirect) {
513 nw->redirect_chars_to_me = wattrs->redirect_chars_to_me;
514 nw->redirect_from = wattrs->redirect_from;
515 }
516
517 attribs.width = pos->width;
518 attribs.height = pos->height;
519 attribs.wclass = GDK_INPUT_OUTPUT;
520 // GDK docs say to not use this. But that's because it's done by GTK...
521 attribs.wmclass_name = GResourceProgramName;
522 attribs.wmclass_class = GResourceProgramName;
523 attribs_mask |= GDK_WA_WMCLASS;
524
525 nw->w = gdk_window_new(gw->w, &attribs, attribs_mask);
526 if (nw->w == NULL) {
527 Log(LOGDEBUG, "GGDKDraw: Failed to create window!");
528 free(nw->window_title);
529 free(nw->ggc);
530 free(nw);
531 return NULL;
532 }
533
534 // We center windows here because we need to know the window size+decor
535 // There is a bug on Windows (all versions < 3.21.1, <= 2.24.30) so don't use GDK_WA_X/GDK_WA_Y
536 // https://bugzilla.gnome.org/show_bug.cgi?id=764996
537 if (nw->is_toplevel && (!(wattrs->mask & wam_positioned) || (wattrs->mask & wam_centered))) {
538 nw->is_centered = true;
539 _GGDKDraw_CenterWindowOnScreen(nw);
540 } else {
541 gdk_window_move(nw->w, nw->pos.x, nw->pos.y);
542 }
543
544 // Set background
545 if (!(wattrs->mask & wam_backcol) || wattrs->background_color == COLOR_DEFAULT) {
546 wattrs->background_color = gdisp->def_background;
547 }
548 nw->ggc->bg = wattrs->background_color;
549 GGDKDrawSetWindowBackground((GWindow)nw, wattrs->background_color);
550
551 if (nw->is_toplevel) {
552 // Set opaque region
553 _GGDKDraw_SetOpaqueRegion(nw);
554
555 // Set icon
556 GGDKWindow icon = gdisp->default_icon;
557 if (((wattrs->mask & wam_icon) && wattrs->icon != NULL) && ((GGDKWindow)wattrs->icon)->is_pixmap) {
558 icon = (GGDKWindow) wattrs->icon;
559 }
560 if (icon != NULL) {
561 GdkPixbuf *pb = gdk_pixbuf_get_from_surface(icon->cs, 0, 0, icon->pos.width, icon->pos.height);
562 if (pb != NULL) {
563 GList_Glib ent = {.data = pb};
564 gdk_window_set_icon_list(nw->w, &ent);
565 g_object_unref(pb);
566 }
567 } else {
568 gdk_window_set_decorations(nw->w, GDK_DECOR_ALL | GDK_DECOR_MENU);
569 }
570
571 GdkGeometry geom = {0};
572 GdkWindowHints hints = 0;
573 if (wattrs->mask & wam_palette) {
574 hints |= GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE;
575 }
576 if ((wattrs->mask & wam_noresize) && wattrs->noresize) {
577 hints |= GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE;
578 }
579
580 // Hmm does this seem right?
581 geom.base_width = geom.min_width = geom.max_width = pos->width;
582 geom.base_height = geom.min_height = geom.max_height = pos->height;
583
584 hints |= GDK_HINT_POS;
585 nw->was_positioned = true;
586
587 gdk_window_set_geometry_hints(nw->w, &geom, hints);
588
589 if ((wattrs->mask & wam_transient) && wattrs->transient != NULL) {
590 GGDKDrawSetTransientFor((GWindow)nw, wattrs->transient);
591 nw->is_dlg = true;
592 } else if (!nw->is_dlg) {
593 ++gdisp->top_window_count;
594 } else if (nw->restrict_input_to_me && gdisp->mru_windows->length > 0) {
595 GGDKDrawSetTransientFor((GWindow)nw, (GWindow) - 1);
596 }
597 nw->isverytransient = (wattrs->mask & wam_verytransient) ? 1 : 0;
598
599 // Don't allow popups or 'very transient' windows from entering this list
600 if (!nw->is_popup && !nw->isverytransient) {
601 // This creates a single link with the data being the window, and next/prev NULL
602 nw->mru_link = g_list_append(NULL, (gpointer)nw);
603 if (nw->mru_link == NULL) {
604 Log(LOGDEBUG, "Failed to create mru_link");
605 gdk_window_destroy(nw->w);
606 free(nw->window_title);
607 free(nw->ggc);
608 free(nw);
609 return NULL;
610 }
611 }
612 }
613
614 // Establish Pango/Cairo context
615 if (!_GGDKDraw_InitPangoCairo(nw)) {
616 gdk_window_destroy(nw->w);
617 free(nw->window_title);
618 free(nw->ggc);
619 free(nw->mru_link);
620 free(nw);
621 return NULL;
622 }
623
624 if ((wattrs->mask & wam_cursor) && wattrs->cursor != ct_default) {
625 GGDKDrawSetCursor((GWindow)nw, wattrs->cursor);
626 }
627
628 // Add a reference to our own structure.
629 GGDKDRAW_ADDREF(nw);
630
631 // Add a reference on the parent window
632 if (nw->parent != gdisp->groot) {
633 GGDKDRAW_ADDREF(nw->parent);
634 }
635
636 if (nw->mru_link) {
637 // Add it into the mru list
638 Log(LOGDEBUG, "Adding %p(%s) to the mru list", nw, nw->window_title);
639 g_queue_push_tail_link(gdisp->mru_windows, nw->mru_link);
640 }
641
642 // Event handler
643 if (eh != NULL) {
644 GEvent e = {0};
645 e.type = et_create;
646 e.w = (GWindow) nw;
647 e.native_window = nw->w;
648 _GGDKDraw_CallEHChecked(nw, &e, eh);
649 }
650
651 // Set the user data to the GWindow
652 // Although there is gdk_window_set_user_data, if non-NULL,
653 // GTK assumes it's a GTKWindow.
654 g_object_set_data(G_OBJECT(nw->w), "GGDKWindow", nw);
655 // Add our reference to the window
656 // This will be unreferenced when the window is destroyed.
657 g_object_ref(G_OBJECT(nw->w));
658 // Add our window to the display's hashmap
659 g_hash_table_add(gdisp->windows, nw);
660 //gdk_window_move_resize(nw->w, nw->pos.x, nw->pos.y, nw->pos.width, nw->pos.height);
661
662 Log(LOGDEBUG, "Window created: %p[%p][%s][toplevel:%d]", nw, nw->w, nw->window_title, nw->is_toplevel);
663 return (GWindow)nw;
664 }
665
_GGDKDraw_NewPixmap(GDisplay * disp,GWindow similar,uint16 width,uint16 height,bool is_bitmap,unsigned char * data)666 static GWindow _GGDKDraw_NewPixmap(GDisplay *disp, GWindow similar, uint16 width, uint16 height, bool is_bitmap,
667 unsigned char *data) {
668 GGDKDisplay *gdisp = (GGDKDisplay *)disp;
669 GGDKWindow gw = (GGDKWindow)calloc(1, sizeof(struct ggdkwindow));
670 if (gw == NULL) {
671 Log(LOGDEBUG, "GGDKDRAW: GGDKWindow calloc failed!");
672 return NULL;
673 }
674
675 gw->ggc = _GGDKDraw_NewGGC();
676 if (gw->ggc == NULL) {
677 Log(LOGDEBUG, "GGDKDRAW: GGC alloc failed!");
678 free(gw);
679 return NULL;
680 }
681 gw->ggc->bg = gdisp->def_background;
682 width &= 0x7fff; // We're always using a cairo surface...
683
684 gw->display = gdisp;
685 gw->is_pixmap = 1;
686 gw->parent = NULL;
687 gw->pos.x = gw->pos.y = 0;
688 gw->pos.width = width;
689 gw->pos.height = height;
690
691 if (data == NULL) {
692 if (similar == NULL) {
693 gw->cs = cairo_image_surface_create(is_bitmap ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_ARGB32, width, height);
694 } else {
695 gw->cs = gdk_window_create_similar_surface(((GGDKWindow)similar)->w, CAIRO_CONTENT_COLOR, width, height);
696 }
697 } else {
698 cairo_format_t format = is_bitmap ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_ARGB32;
699 gw->cs = cairo_image_surface_create_for_data(data, format, width, height, cairo_format_stride_for_width(format, width));
700 }
701
702 if (gw->cs == NULL) {
703 Log(LOGDEBUG, "GGDKDRAW: Cairo image surface creation failed!");
704 free(gw->ggc);
705 free(gw);
706 return NULL;
707 }
708
709 if (!_GGDKDraw_InitPangoCairo(gw)) {
710 cairo_surface_destroy(gw->cs);
711 free(gw->ggc);
712 free(gw);
713 return NULL;
714 }
715 // Add a reference to ourselves
716 GGDKDRAW_ADDREF(gw);
717 return (GWindow)gw;
718 }
719
_GGDKDraw_GdkModifierToKsm(GdkModifierType mask)720 static int16 _GGDKDraw_GdkModifierToKsm(GdkModifierType mask) {
721 int16 state = 0;
722 //Translate from mask to X11 state
723 if (mask & GDK_SHIFT_MASK) {
724 state |= ksm_shift;
725 }
726 if (mask & GDK_LOCK_MASK) {
727 state |= ksm_capslock;
728 }
729 if (mask & GDK_CONTROL_MASK) {
730 state |= ksm_control;
731 }
732 if (mask & GDK_MOD1_MASK) { //Guess
733 state |= ksm_meta;
734 }
735 if (mask & GDK_MOD2_MASK) { //Guess
736 state |= ksm_cmdmacosx;
737 }
738 if (mask & GDK_SUPER_MASK) {
739 state |= ksm_super;
740 }
741 if (mask & GDK_HYPER_MASK) {
742 state |= ksm_hyper;
743 }
744 //ksm_option?
745 if (mask & GDK_BUTTON1_MASK) {
746 state |= ksm_button1;
747 }
748 if (mask & GDK_BUTTON2_MASK) {
749 state |= ksm_button2;
750 }
751 if (mask & GDK_BUTTON3_MASK) {
752 state |= ksm_button3;
753 }
754 if (mask & GDK_BUTTON4_MASK) {
755 state |= ksm_button4;
756 }
757 if (mask & GDK_BUTTON5_MASK) {
758 state |= ksm_button5;
759 }
760
761 return state;
762 }
763
_GGDKDraw_GdkVisibilityStateToVS(GdkVisibilityState state)764 static VisibilityState _GGDKDraw_GdkVisibilityStateToVS(GdkVisibilityState state) {
765 switch (state) {
766 case GDK_VISIBILITY_FULLY_OBSCURED:
767 return vs_obscured;
768 break;
769 case GDK_VISIBILITY_PARTIAL:
770 return vs_partially;
771 break;
772 case GDK_VISIBILITY_UNOBSCURED:
773 default:
774 return vs_unobscured;
775 break;
776 }
777 }
778
_GGDKDraw_WindowOrParentsDying(GGDKWindow gw)779 static int _GGDKDraw_WindowOrParentsDying(GGDKWindow gw) {
780 while (gw != NULL) {
781 if (gw->is_dying) {
782 return true;
783 }
784 if (gw->is_toplevel) {
785 return false;
786 }
787 gw = gw->parent;
788 }
789 return false;
790 }
791
_GGDKDraw_FilterByModal(GdkEvent * event,GGDKWindow gw)792 static bool _GGDKDraw_FilterByModal(GdkEvent *event, GGDKWindow gw) {
793 switch (event->type) {
794 case GDK_KEY_PRESS:
795 case GDK_KEY_RELEASE:
796 case GDK_BUTTON_PRESS:
797 case GDK_BUTTON_RELEASE:
798 case GDK_SCROLL:
799 case GDK_MOTION_NOTIFY:
800 case GDK_DELETE:
801 case GDK_FOCUS_CHANGE:
802 break;
803 default:
804 return false;
805 break;
806 }
807
808 GGDKWindow gww = gw;
809 GPtrArray *stack = gw->display->transient_stack;
810
811 if (gww && gw->display->restrict_count == 0) {
812 return false;
813 }
814
815 GGDKWindow last_modal = NULL;
816 while (gww != NULL) {
817 if (gww->is_toplevel) {
818 for (int i = ((int)stack->len) - 1; i >= 0; --i) {
819 GGDKWindow ow = (GGDKWindow)stack->pdata[i];
820 if (ow == gww || ow->restrict_input_to_me) { // fudged
821 last_modal = ow;
822 break;
823 }
824 }
825
826 if (last_modal == NULL || last_modal == gww) {
827 return false;
828 }
829 }
830
831 gww = gww->parent;
832 }
833
834 if (event->type != GDK_MOTION_NOTIFY && event->type != GDK_BUTTON_RELEASE && event->type != GDK_FOCUS_CHANGE) {
835 gdk_window_beep(gw->w);
836 if (last_modal != NULL) {
837 GDrawRaise((GWindow)last_modal);
838 }
839 }
840
841 return true;
842 }
843
_GGDKDraw_ProcessTimerEvent(gpointer user_data)844 static gboolean _GGDKDraw_ProcessTimerEvent(gpointer user_data) {
845 GGDKTimer *timer = (GGDKTimer *)user_data;
846 GEvent e = {0};
847 bool ret = true;
848
849 if (!timer->active || _GGDKDraw_WindowOrParentsDying((GGDKWindow)timer->owner)) {
850 timer->active = false;
851 timer->stopped = true;
852 return false;
853 }
854
855 e.type = et_timer;
856 e.w = timer->owner;
857 e.native_window = timer->owner->native_window;
858 e.u.timer.timer = (GTimer *)timer;
859 e.u.timer.userdata = timer->userdata;
860
861 GGDKDRAW_ADDREF(timer);
862 _GGDKDraw_CallEHChecked((GGDKWindow)timer->owner, &e, timer->owner->eh);
863 if (timer->active) {
864 if (timer->repeat_time == 0) {
865 timer->stopped = true; // Since we return false, this timer is no longer valid.
866 GGDKDrawCancelTimer((GTimer *)timer);
867 ret = false;
868 } else if (timer->has_differing_repeat_time) {
869 timer->has_differing_repeat_time = false;
870 timer->glib_timeout_id = g_timeout_add(timer->repeat_time, _GGDKDraw_ProcessTimerEvent, timer);
871 ret = false;
872 }
873 }
874
875 GGDKDRAW_DECREF(timer, _GGDKDraw_OnTimerDestroyed);
876 return ret;
877 }
878
_GGDKDraw_DispatchEvent(GdkEvent * event,gpointer data)879 static void _GGDKDraw_DispatchEvent(GdkEvent *event, gpointer data) {
880 //static int request_id = 0;
881 struct gevent gevent = {0};
882 GGDKDisplay *gdisp = (GGDKDisplay *)data;
883 GdkWindow *w = ((GdkEventAny *)event)->window;
884 GGDKWindow gw;
885 guint32 event_time = gdk_event_get_time(event);
886 if (event_time != GDK_CURRENT_TIME) {
887 gdisp->last_event_time = event_time;
888 }
889
890 //Log(LOGDEBUG, "[%d] Received event %d(%s) %p", request_id++, event->type, GdkEventName(event->type), w);
891 //fflush(stderr);
892
893 if (w == NULL) {
894 return;
895 } else if ((gw = g_object_get_data(G_OBJECT(w), "GGDKWindow")) == NULL) {
896 //Log(LOGDEBUG, "MISSING GW!");
897 return;
898 } else if (_GGDKDraw_WindowOrParentsDying(gw) || gdk_window_is_destroyed(w)) {
899 Log(LOGDEBUG, "DYING! %p", w);
900 return;
901 } else if (_GGDKDraw_FilterByModal(event, gw)) {
902 Log(LOGDEBUG, "Discarding event - has modals!");
903 return;
904 }
905
906 gevent.w = (GWindow)gw;
907 gevent.native_window = (void *)gw->w;
908 gevent.type = et_noevent;
909
910 switch (event->type) {
911 case GDK_KEY_PRESS:
912 case GDK_KEY_RELEASE: {
913 GdkEventKey *key = (GdkEventKey *)event;
914 gevent.type = event->type == GDK_KEY_PRESS ? et_char : et_charup;
915 gevent.u.chr.state = _GGDKDraw_GdkModifierToKsm(((GdkEventKey *)event)->state);
916
917 #ifdef GDK_WINDOWING_QUARTZ
918 // On Mac, the Alt/Option key is used for alternate input.
919 // We want accelerators, so translate ourselves, forcing the group to 0.
920 if ((gevent.u.chr.state & ksm_meta) && key->group != 0) {
921 GdkKeymap *km = gdk_keymap_get_for_display(gdisp->display);
922 guint keyval;
923
924 gdk_keymap_translate_keyboard_state(km, key->hardware_keycode,
925 key->state, 0, &keyval, NULL, NULL, NULL);
926
927 //Log(LOGDEBUG, "Fixed keyval from 0x%x(%s) -> 0x%x(%s)",
928 // key->keyval, gdk_keyval_name(key->keyval),
929 // keyval, gdk_keyval_name(keyval));
930
931 key->keyval = keyval;
932 }
933 #endif
934
935 gevent.u.chr.autorepeat =
936 event->type == GDK_KEY_PRESS &&
937 gdisp->ks.type == GDK_KEY_PRESS &&
938 key->keyval == gdisp->ks.keyval &&
939 key->state == gdisp->ks.state;
940 // Mumble mumble Mac
941 //if ((event->xkey.state & ksm_option) && gdisp->macosx_cmd) {
942 // gevent.u.chr.state |= ksm_meta;
943 //}
944
945 if (gevent.u.chr.autorepeat && GKeysymIsModifier(key->keyval)) {
946 gevent.type = et_noevent;
947 } else {
948 if (key->keyval == GDK_KEY_space) {
949 gw->display->is_space_pressed = event->type == GDK_KEY_PRESS;
950 }
951
952 gevent.u.chr.keysym = key->keyval;
953 gevent.u.chr.chars[0] = gdk_keyval_to_unicode(key->keyval);
954 }
955
956 gdisp->ks.type = key->type;
957 gdisp->ks.keyval = key->keyval;
958 gdisp->ks.state = key->state;
959 }
960 break;
961 case GDK_MOTION_NOTIFY: {
962 GdkEventMotion *evt = (GdkEventMotion *)event;
963 gevent.type = et_mousemove;
964 gevent.u.mouse.state = _GGDKDraw_GdkModifierToKsm(evt->state);
965 gevent.u.mouse.x = evt->x;
966 gevent.u.mouse.y = evt->y;
967 //Log(LOGDEBUG, "Motion: [%f %f]", evt->x, evt->y);
968 }
969 break;
970 case GDK_SCROLL: { //Synthesize a button press
971 GdkEventScroll *evt = (GdkEventScroll *)event;
972 gevent.u.mouse.state = _GGDKDraw_GdkModifierToKsm(evt->state);
973 gevent.u.mouse.x = evt->x;
974 gevent.u.mouse.y = evt->y;
975 gevent.u.mouse.clicks = gdisp->bs.cur_click = 1;
976 gevent.type = et_mousedown;
977
978 switch (evt->direction) {
979 case GDK_SCROLL_UP:
980 gevent.u.mouse.button = 4;
981 break;
982 case GDK_SCROLL_DOWN:
983 gevent.u.mouse.button = 5;
984 break;
985 case GDK_SCROLL_LEFT:
986 gevent.u.mouse.button = 6;
987 break;
988 case GDK_SCROLL_RIGHT:
989 gevent.u.mouse.button = 7;
990 break;
991 default: // Ignore GDK_SCROLL_SMOOTH
992 gevent.type = et_noevent;
993 }
994 // We need to simulate two events... I think.
995 if (gevent.type != et_noevent) {
996 GGDKDrawPostEvent(&gevent);
997 gevent.type = et_mouseup;
998 }
999 }
1000 break;
1001 case GDK_BUTTON_PRESS:
1002 case GDK_BUTTON_RELEASE: {
1003 GdkEventButton *evt = (GdkEventButton *)event;
1004 gevent.u.mouse.state = _GGDKDraw_GdkModifierToKsm(evt->state);
1005 gevent.u.mouse.x = evt->x;
1006 gevent.u.mouse.y = evt->y;
1007 gevent.u.mouse.button = evt->button;
1008 gevent.u.mouse.time = evt->time;
1009
1010 Log(LOGDEBUG, "Button %7s: [%f %f]", evt->type == GDK_BUTTON_PRESS ? "press" : "release", evt->x, evt->y);
1011
1012 #ifdef GDK_WINDOWING_QUARTZ
1013 // Quartz backend fails to give a button press event
1014 // https://bugzilla.gnome.org/show_bug.cgi?id=769961
1015 if (!evt->send_event && evt->type == GDK_BUTTON_RELEASE &&
1016 gdisp->bs.release_w == gw &&
1017 (evt->x < 0 || evt->x > gw->pos.width ||
1018 evt->y < 0 || evt->y > gw->pos.height)) {
1019 evt->send_event = true;
1020 gdk_event_put(event);
1021 event->type = GDK_BUTTON_PRESS;
1022 }
1023 #endif
1024
1025 if (event->type == GDK_BUTTON_PRESS) {
1026 int xdiff, ydiff;
1027 gevent.type = et_mousedown;
1028
1029 xdiff = abs(((int)evt->x) - gdisp->bs.release_x);
1030 ydiff = abs(((int)evt->y) - gdisp->bs.release_y);
1031
1032 if (xdiff + ydiff < gdisp->bs.double_wiggle &&
1033 gw == gdisp->bs.release_w &&
1034 gevent.u.mouse.button == gdisp->bs.release_button &&
1035 (int32_t)(gevent.u.mouse.time - gdisp->bs.last_press_time) < gdisp->bs.double_time &&
1036 gevent.u.mouse.time >= gdisp->bs.last_press_time) { // Time can wrap
1037
1038 gdisp->bs.cur_click++;
1039 } else {
1040 gdisp->bs.cur_click = 1;
1041 }
1042 gdisp->bs.last_press_time = gevent.u.mouse.time;
1043 } else {
1044 gevent.type = et_mouseup;
1045 gdisp->bs.release_w = gw;
1046 gdisp->bs.release_x = evt->x;
1047 gdisp->bs.release_y = evt->y;
1048 gdisp->bs.release_button = evt->button;
1049 }
1050 gevent.u.mouse.clicks = gdisp->bs.cur_click;
1051 }
1052 break;
1053 case GDK_EXPOSE: {
1054 GdkEventExpose *expose = (GdkEventExpose *)event;
1055 // Okay. So on GDK3, we must exclude child window regions to prevent flicker.
1056 // However, we cannot modify the expose event's region directly, as this is
1057 // used by GDK to determine what child windows to invalidate. Hence, we must
1058 // make a copy of the region, sans any child window areas.
1059 cairo_rectangle_int_t extents;
1060 cairo_region_t *reg = _GGDKDraw_ExcludeChildRegions(gw, expose->region, true);
1061 cairo_region_get_extents(reg, &extents);
1062
1063 gevent.type = et_expose;
1064 gevent.u.expose.rect.x = extents.x;
1065 gevent.u.expose.rect.y = extents.y;
1066 gevent.u.expose.rect.width = extents.width;
1067 gevent.u.expose.rect.height = extents.height;
1068
1069 // This can happen if say gdk_window_raise is called, which then runs the
1070 // event loop itself, which is outside of our checked event handler
1071 // I've added autopaint cleanups before such calls, so this should
1072 // never happen any more.
1073 assert(gw->cc == NULL);
1074
1075 #ifdef GGDKDRAW_GDK_3_22
1076 gw->drawing_ctx = gdk_window_begin_draw_frame(w, reg);
1077 #else
1078 gdk_window_begin_paint_region(w, reg);
1079 #endif
1080 gw->is_in_paint = true;
1081 gdisp->dirty_window = gw;
1082
1083 // But wait, there's more! On GDK3, if the window isn't native,
1084 // gdk_window_begin_paint_region does nothing.
1085 // So we have to (a) ensure we draw to an offscreen surface,
1086 // and (b) clip the region appropriately. But on Windows
1087 // (and maybe Quartz), it seems to double buffer anyway.
1088 if (!gdk_window_has_native(gw->w)) {
1089 #if !defined(GDK_WINDOWING_WIN32) && !defined(GDK_WINDOWING_QUARTZ)
1090 double sx = 1, sy = 1;
1091
1092 gw->cs = gdk_window_create_similar_surface(gw->w, CAIRO_CONTENT_COLOR, extents.width, extents.height);
1093 #if (CAIRO_VERSION_MAJOR >= 2) || (CAIRO_VERSION_MAJOR >= 1 && CAIRO_VERSION_MINOR >= 14)
1094 cairo_surface_get_device_scale(gw->cs, &sx, &sy);
1095 #endif
1096 cairo_surface_set_device_offset(gw->cs, -extents.x * sx, -extents.y * sy);
1097 gw->cc = cairo_create(gw->cs);
1098 gw->expose_region = cairo_region_reference(reg);
1099 #else
1100 #ifdef GGDKDRAW_GDK_3_22
1101 gw->cc = cairo_reference(gdk_drawing_context_get_cairo_context(gw->drawing_ctx));
1102 #else
1103 gw->cc = gdk_cairo_create(gw->w);
1104 #endif
1105 #endif
1106 gdk_cairo_region(gw->cc, reg);
1107 cairo_clip(gw->cc);
1108 }
1109 cairo_region_destroy(reg);
1110 }
1111 break;
1112 case GDK_VISIBILITY_NOTIFY:
1113 gevent.type = et_visibility;
1114 gevent.u.visibility.state = _GGDKDraw_GdkVisibilityStateToVS(((GdkEventVisibility *)event)->state);
1115 break;
1116 case GDK_FOCUS_CHANGE:
1117 gevent.type = et_focus;
1118 gevent.u.focus.gained_focus = ((GdkEventFocus *)event)->in;
1119 gevent.u.focus.mnemonic_focus = false;
1120
1121 Log(LOGDEBUG, "Focus change %s %p(%s %d %d)",
1122 gevent.u.focus.gained_focus ? "IN" : "OUT",
1123 gw, gw->window_title, gw->is_toplevel, gw->istransient);
1124
1125 if (gw->mru_link && gevent.u.focus.gained_focus) {
1126 GGDKWindow cur_toplevel = (GGDKWindow)g_queue_peek_head(gdisp->mru_windows);
1127 if (cur_toplevel != gw) {
1128 Log(LOGDEBUG, "Last active toplevel updated: %p(%s)", gw, gw->window_title);
1129 g_queue_unlink(gdisp->mru_windows, gw->mru_link);
1130 g_queue_push_head_link(gdisp->mru_windows, gw->mru_link);
1131 }
1132 }
1133
1134 break;
1135 case GDK_ENTER_NOTIFY:
1136 case GDK_LEAVE_NOTIFY: { // Should only get this on top level
1137 GdkEventCrossing *crossing = (GdkEventCrossing *)event;
1138 if (crossing->focus) { //Focus or inferior
1139 break;
1140 }
1141 if (gdisp->focusfollowsmouse && gw != NULL && gw->eh != NULL) {
1142 gevent.type = et_focus;
1143 gevent.u.focus.gained_focus = crossing->type == GDK_ENTER_NOTIFY;
1144 gevent.u.focus.mnemonic_focus = false;
1145 _GGDKDraw_CallEHChecked(gw, &gevent, gw->eh);
1146 }
1147 gevent.type = et_crossing;
1148 gevent.u.crossing.x = crossing->x;
1149 gevent.u.crossing.y = crossing->y;
1150 gevent.u.crossing.state = _GGDKDraw_GdkModifierToKsm(crossing->state);
1151 gevent.u.crossing.entered = crossing->type == GDK_ENTER_NOTIFY;
1152 gevent.u.crossing.device = NULL;
1153 gevent.u.crossing.time = crossing->time;
1154
1155 // Always set to false when crossing boundary...
1156 gw->display->is_space_pressed = false;
1157 }
1158 break;
1159 case GDK_CONFIGURE: {
1160 GdkEventConfigure *configure = (GdkEventConfigure *)event;
1161 gevent.type = et_resize;
1162 gevent.u.resize.size.x = configure->x;
1163 gevent.u.resize.size.y = configure->y;
1164 gevent.u.resize.size.width = configure->width;
1165 gevent.u.resize.size.height = configure->height;
1166 gevent.u.resize.dx = configure->x - gw->pos.x;
1167 gevent.u.resize.dy = configure->y - gw->pos.y;
1168 gevent.u.resize.dwidth = configure->width - gw->pos.width;
1169 gevent.u.resize.dheight = configure->height - gw->pos.height;
1170 gevent.u.resize.moved = gevent.u.resize.sized = false;
1171 if (gevent.u.resize.dx != 0 || gevent.u.resize.dy != 0) {
1172 gevent.u.resize.moved = true;
1173 gw->is_centered = false;
1174 }
1175 if (gevent.u.resize.dwidth != 0 || gevent.u.resize.dheight != 0) {
1176 gevent.u.resize.sized = true;
1177 }
1178
1179 // I could make this Windows specific... But it doesn't seem necessary on other platforms too.
1180 // On Windows, repeated configure messages are sent if we move the window around.
1181 // This causes CPU usage to go up because mouse handlers of this message just redraw the whole window.
1182 if (gw->is_toplevel && !gevent.u.resize.sized && gevent.u.resize.moved) {
1183 gevent.type = et_noevent;
1184 Log(LOGDEBUG, "Configure DISCARDED: %p:%s, %d %d %d %d", gw, gw->window_title, gw->pos.x, gw->pos.y, gw->pos.width, gw->pos.height);
1185 } else {
1186 Log(LOGDEBUG, "CONFIGURED: %p:%s, %d %d %d %d", gw, gw->window_title, gw->pos.x, gw->pos.y, gw->pos.width, gw->pos.height);
1187 }
1188 gw->pos = gevent.u.resize.size;
1189
1190 // Update the opaque region (we're always completely opaque)
1191 // Although I don't actually know if this is completely necessary
1192 if (gw->is_toplevel && gevent.u.resize.sized) {
1193 _GGDKDraw_SetOpaqueRegion(gw);
1194 }
1195 }
1196 break;
1197 case GDK_MAP:
1198 gevent.type = et_map;
1199 gevent.u.map.is_visible = true;
1200 gw->is_visible = true;
1201 break;
1202 case GDK_UNMAP:
1203 gevent.type = et_map;
1204 gevent.u.map.is_visible = false;
1205 gw->is_visible = false;
1206 break;
1207 case GDK_DESTROY:
1208 GGDKDrawDestroyWindow((GWindow)gw); //Note: If we get here, something's probably wrong.
1209 break;
1210 case GDK_DELETE:
1211 gw->is_waiting_for_selection = false;
1212 gevent.type = et_close;
1213 break;
1214 case GDK_SELECTION_CLEAR: {
1215 gevent.type = et_selclear;
1216 gevent.u.selclear.sel = sn_primary;
1217 for (int i = 0; i < sn_max; i++) {
1218 GdkEventSelection *se = (GdkEventSelection *)event;
1219 if (se->selection == gdisp->selinfo[i].sel_atom) {
1220 gevent.u.selclear.sel = i;
1221 break;
1222 }
1223 }
1224 _GGDKDraw_ClearSelData(gdisp, gevent.u.selclear.sel);
1225 }
1226 break;
1227 case GDK_SELECTION_REQUEST: {
1228 GdkEventSelection *e = (GdkEventSelection *)event;
1229 gdk_selection_send_notify(
1230 e->requestor, e->selection, e->target,
1231 _GGDKDraw_TransmitSelection(gdisp, e) ? e->property : GDK_NONE,
1232 e->time);
1233 }
1234 break;
1235 case GDK_SELECTION_NOTIFY: // paste
1236 if (gw->is_waiting_for_selection) {
1237 gw->is_waiting_for_selection = false;
1238 gw->is_notified_of_selection = ((GdkEventSelection *)event)->property != GDK_NONE;
1239 }
1240 break;
1241 case GDK_PROPERTY_NOTIFY:
1242 break;
1243 default:
1244 Log(LOGDEBUG, "UNPROCESSED GDK EVENT %d %s", event->type, GdkEventName(event->type));
1245 break;
1246 }
1247
1248 if (gevent.type != et_noevent && gw != NULL && gw->eh != NULL) {
1249 _GGDKDraw_CallEHChecked(gw, &gevent, gw->eh);
1250 }
1251 //Log(LOGDEBUG, "[%d] Finished processing %d(%s)", request_id++, event->type, GdkEventName(event->type));
1252 }
1253
GGDKDrawInit(GDisplay * gdisp)1254 static void GGDKDrawInit(GDisplay *gdisp) {
1255 FState *fs = calloc(1, sizeof(FState));
1256 if (fs == NULL) {
1257 Log(LOGDEBUG, "GGDKDraw: FState alloc failed!");
1258 assert(false);
1259 }
1260
1261 // In inches, because that's how fonts are measured
1262 gdisp->fontstate = fs;
1263 fs->res = gdisp->res;
1264 }
1265
GGDKDrawSetDefaultIcon(GWindow icon)1266 static void GGDKDrawSetDefaultIcon(GWindow icon) {
1267 GGDKWindow gicon = (GGDKWindow)icon;
1268 if (gicon->is_pixmap) {
1269 gicon->display->default_icon = gicon;
1270 }
1271 }
1272
GGDKDrawCreateTopWindow(GDisplay * gdisp,GRect * pos,int (* eh)(GWindow gw,GEvent *),void * user_data,GWindowAttrs * gattrs)1273 static GWindow GGDKDrawCreateTopWindow(GDisplay *gdisp, GRect *pos, int (*eh)(GWindow gw, GEvent *), void *user_data,
1274 GWindowAttrs *gattrs) {
1275 Log(LOGDEBUG, " ");
1276 return _GGDKDraw_CreateWindow((GGDKDisplay *) gdisp, NULL, pos, eh, user_data, gattrs);
1277 }
1278
GGDKDrawCreateSubWindow(GWindow gw,GRect * pos,int (* eh)(GWindow gw,GEvent *),void * user_data,GWindowAttrs * gattrs)1279 static GWindow GGDKDrawCreateSubWindow(GWindow gw, GRect *pos, int (*eh)(GWindow gw, GEvent *), void *user_data,
1280 GWindowAttrs *gattrs) {
1281 Log(LOGDEBUG, " ");
1282 return _GGDKDraw_CreateWindow(((GGDKWindow) gw)->display, (GGDKWindow) gw, pos, eh, user_data, gattrs);
1283 }
1284
GGDKDrawCreatePixmap(GDisplay * gdisp,GWindow similar,uint16 width,uint16 height)1285 static GWindow GGDKDrawCreatePixmap(GDisplay *gdisp, GWindow similar, uint16 width, uint16 height) {
1286 Log(LOGDEBUG, " ");
1287
1288 //TODO: Check format?
1289 return _GGDKDraw_NewPixmap(gdisp, similar, width, height, false, NULL);
1290 }
1291
GGDKDrawCreateBitmap(GDisplay * gdisp,uint16 width,uint16 height,uint8 * data)1292 static GWindow GGDKDrawCreateBitmap(GDisplay *gdisp, uint16 width, uint16 height, uint8 *data) {
1293 Log(LOGDEBUG, " ");
1294 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
1295 int actual = (width & 0x7fff) / 8;
1296
1297 if (actual != stride) {
1298 GWindow ret = _GGDKDraw_NewPixmap(gdisp, NULL, width, height, true, NULL);
1299 if (ret == NULL) {
1300 return NULL;
1301 }
1302
1303 cairo_surface_flush(((GGDKWindow)ret)->cs);
1304 uint8 *buf = cairo_image_surface_get_data(((GGDKWindow)ret)->cs);
1305 for (int j = 0; j < height; j++) {
1306 memcpy(buf + stride * j, data + actual * j, actual);
1307 }
1308 cairo_surface_mark_dirty(((GGDKWindow)ret)->cs);
1309 return ret;
1310 }
1311
1312 return _GGDKDraw_NewPixmap(gdisp, NULL, width, height, true, data);
1313 }
1314
GGDKDrawCreateCursor(GWindow src,GWindow mask,Color fg,Color bg,int16 x,int16 y)1315 static GCursor GGDKDrawCreateCursor(GWindow src, GWindow mask, Color fg, Color bg, int16 x, int16 y) {
1316 Log(LOGDEBUG, " ");
1317
1318 GGDKDisplay *gdisp = (GGDKDisplay *)(src->display);
1319 GdkCursor *cursor = NULL;
1320 if (mask == NULL) { // Use src directly
1321 assert(src != NULL);
1322 assert(src->is_pixmap);
1323 cursor = gdk_cursor_new_from_surface(gdisp->display, ((GGDKWindow)src)->cs, x, y);
1324 } else { // Assume it's an X11-style cursor
1325 cairo_surface_t *cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, src->pos.width, src->pos.height);
1326 cairo_t *cc = cairo_create(cs);
1327
1328 // Masking
1329 //Background
1330 cairo_set_source_rgb(cc, COLOR_RED(bg) / 255., COLOR_GREEN(bg) / 255., COLOR_BLUE(bg) / 255.);
1331 cairo_mask_surface(cc, ((GGDKWindow)mask)->cs, 0, 0);
1332 //Foreground
1333 cairo_set_source_rgb(cc, COLOR_RED(fg) / 255., COLOR_GREEN(fg) / 255., COLOR_BLUE(fg) / 255.);
1334 cairo_mask_surface(cc, ((GGDKWindow)src)->cs, 0, 0);
1335
1336 cursor = gdk_cursor_new_from_surface(gdisp->display, cs, x, y);
1337
1338 cairo_destroy(cc);
1339 cairo_surface_destroy(cs);
1340 }
1341
1342 g_ptr_array_add(gdisp->cursors, cursor);
1343 return ct_user + (gdisp->cursors->len - 1);
1344 }
1345
GGDKDrawDestroyCursor(GDisplay * disp,GCursor gcursor)1346 static void GGDKDrawDestroyCursor(GDisplay *disp, GCursor gcursor) {
1347 Log(LOGDEBUG, " ");
1348
1349 GGDKDisplay *gdisp = (GGDKDisplay *)disp;
1350 gcursor -= ct_user;
1351 if ((int)gcursor >= 0 && gcursor < gdisp->cursors->len) {
1352 g_object_unref(gdisp->cursors->pdata[gcursor]);
1353 gdisp->cursors->pdata[gcursor] = NULL;
1354 }
1355 }
1356
GGDKDrawDestroyWindow(GWindow w)1357 static void GGDKDrawDestroyWindow(GWindow w) {
1358 GGDKWindow gw = (GGDKWindow) w;
1359 Log(LOGDEBUG, "%p[%p][%s][toplevel:%d][pixmap:%d]",
1360 gw, gw->w, gw->window_title, gw->is_toplevel, gw->is_pixmap);
1361
1362 if (gw->is_dying) {
1363 return;
1364 }
1365
1366 gw->is_dying = true;
1367 gw->is_waiting_for_selection = false;
1368 gw->is_notified_of_selection = false;
1369
1370 if (!gw->is_pixmap && gw != gw->display->groot) {
1371 GList_Glib *list = gdk_window_get_children(gw->w);
1372 GList_Glib *child = list;
1373 while (child != NULL) {
1374 GWindow c = g_object_get_data(child->data, "GGDKWindow");
1375 assert(c != NULL);
1376 GGDKDrawDestroyWindow(c);
1377 child = child->next;
1378 }
1379 g_list_free(list);
1380
1381 if (gw->is_toplevel) {
1382 // Remove itself from the transient list, if present
1383 GGDKDrawSetTransientFor((GWindow)gw, NULL);
1384
1385 // Remove it from the mru window list
1386 if (gw->mru_link != NULL) {
1387 Log(LOGDEBUG, "Removing %p(%s) from mru window list", gw, gw->window_title);
1388 g_queue_delete_link(gw->display->mru_windows, gw->mru_link);
1389 gw->mru_link = NULL;
1390 }
1391 // This is so that e.g. popup menus get hidden immediately
1392 // On Quartz, this is also necessary for toplevel windows,
1393 // as focus change events are not received when a window
1394 // is destroyed, but they *are* when it's first hidden...
1395 GDrawSetVisible((GWindow)gw, false);
1396
1397 // HACK! Reparent all windows transient to this
1398 // If they were truly transient in the normal sense, they would just be
1399 // destroyed when this window goes away
1400 for (int i = ((int)gw->display->transient_stack->len) - 1; i >= 0; --i) {
1401 GGDKWindow tw = (GGDKWindow)gw->display->transient_stack->pdata[i];
1402 if (tw->transient_owner == gw) {
1403 Log(LOGWARN, "Resetting transient owner on %p(%s) as %p(%s) is dying",
1404 tw, tw->window_title, gw, gw->window_title);
1405 GGDKDrawSetTransientFor((GWindow)tw, (GWindow)-1);
1406 }
1407 }
1408 }
1409
1410 if (gw->display->last_dd.w == gw) {
1411 gw->display->last_dd.w = NULL;
1412 }
1413 // Ensure an invalid value is returned if someone tries to get this value again.
1414 g_object_set_data(G_OBJECT(gw->w), "GGDKWindow", NULL);
1415 }
1416 GGDKDRAW_DECREF(gw, _GGDKDraw_InitiateWindowDestroy);
1417 }
1418
GGDKDrawNativeWindowExists(GDisplay * UNUSED (gdisp),void * native_window)1419 static int GGDKDrawNativeWindowExists(GDisplay *UNUSED(gdisp), void *native_window) {
1420 //Log(LOGDEBUG, " ");
1421 GdkWindow *w = (GdkWindow *)native_window;
1422
1423 if (w != NULL) {
1424 GGDKWindow gw = g_object_get_data(G_OBJECT(w), "GGDKWindow");
1425 // So if the window is dying, the gdk window is already gone.
1426 // But gcontainer.c expects this to return true on et_destroy...
1427 if (gw == NULL || gw->is_dying) {
1428 return true;
1429 } else {
1430 return !gdk_window_is_destroyed(w);
1431 }
1432 }
1433 return false;
1434 }
1435
GGDKDrawSetZoom(GWindow UNUSED (gw),GRect * UNUSED (size),enum gzoom_flags UNUSED (flags))1436 static void GGDKDrawSetZoom(GWindow UNUSED(gw), GRect *UNUSED(size), enum gzoom_flags UNUSED(flags)) {
1437 //Log(LOGDEBUG, " ");
1438 // Not implemented.
1439 }
1440
GGDKDrawSetWindowBackground(GWindow w,Color gcol)1441 static void GGDKDrawSetWindowBackground(GWindow w, Color gcol) {
1442 Log(LOGDEBUG, " ");
1443 GGDKWindow gw = (GGDKWindow)w;
1444 GdkRGBA col = {
1445 .red = COLOR_RED(gcol) / 255.,
1446 .green = COLOR_GREEN(gcol) / 255.,
1447 .blue = COLOR_BLUE(gcol) / 255.,
1448 .alpha = 1.
1449 };
1450 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1451 gdk_window_set_background_rgba(gw->w, &col);
1452 G_GNUC_END_IGNORE_DEPRECATIONS
1453 }
1454
GGDKDrawSetDither(GDisplay * UNUSED (gdisp),int UNUSED (set))1455 static int GGDKDrawSetDither(GDisplay *UNUSED(gdisp), int UNUSED(set)) {
1456 // Not implemented; does nothing.
1457 return false;
1458 }
1459
GGDKDrawSetVisible(GWindow w,int show)1460 static void GGDKDrawSetVisible(GWindow w, int show) {
1461 Log(LOGDEBUG, "0x%p %d", w, show);
1462 GGDKWindow gw = (GGDKWindow)w;
1463 _GGDKDraw_CleanupAutoPaint(gw->display);
1464 if (show) {
1465 if (gdk_window_is_visible(gw->w)) {
1466 Log(LOGDEBUG, "0x%p %d DISCARDED", w, show);
1467 return;
1468 }
1469 #ifdef GDK_WINDOWING_QUARTZ
1470 // Quartz backend fails to send a configure event after showing a window
1471 // But FF expects one.
1472 _GGDKDraw_OnFakedConfigure(gw);
1473 #endif
1474 gdk_window_show(gw->w);
1475 if (gw->restrict_input_to_me && gw->transient_owner == NULL && gw->display->mru_windows->length > 0) {
1476 GGDKDrawSetTransientFor((GWindow)gw, (GWindow) - 1);
1477 }
1478 } else {
1479 GGDKDrawSetTransientFor((GWindow)gw, NULL);
1480 gdk_window_hide(gw->w);
1481 }
1482 }
1483
GGDKDrawMove(GWindow gw,int32 x,int32 y)1484 static void GGDKDrawMove(GWindow gw, int32 x, int32 y) {
1485 Log(LOGDEBUG, "%p:%s, %d %d", gw, ((GGDKWindow)gw)->window_title, x, y);
1486 _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1487 gdk_window_move(((GGDKWindow)gw)->w, x, y);
1488 ((GGDKWindow)gw)->is_centered = false;
1489 if (!gw->is_toplevel) {
1490 _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1491 }
1492 }
1493
GGDKDrawTrueMove(GWindow w,int32 x,int32 y)1494 static void GGDKDrawTrueMove(GWindow w, int32 x, int32 y) {
1495 Log(LOGDEBUG, " ");
1496 GGDKDrawMove(w, x, y);
1497 }
1498
GGDKDrawResize(GWindow gw,int32 w,int32 h)1499 static void GGDKDrawResize(GWindow gw, int32 w, int32 h) {
1500 Log(LOGDEBUG, "%p:%s, %d %d", gw, ((GGDKWindow)gw)->window_title, w, h);
1501 _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1502 gdk_window_resize(((GGDKWindow)gw)->w, w, h);
1503 if (gw->is_toplevel && ((GGDKWindow)gw)->is_centered) {
1504 _GGDKDraw_CenterWindowOnScreen((GGDKWindow)gw);
1505 }
1506 if (!gw->is_toplevel) {
1507 _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1508 }
1509 }
1510
GGDKDrawMoveResize(GWindow gw,int32 x,int32 y,int32 w,int32 h)1511 static void GGDKDrawMoveResize(GWindow gw, int32 x, int32 y, int32 w, int32 h) {
1512 Log(LOGDEBUG, "%p:%s, %d %d %d %d", gw, ((GGDKWindow)gw)->window_title, x, y, w, h);
1513 _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1514 gdk_window_move_resize(((GGDKWindow)gw)->w, x, y, w, h);
1515 if (!gw->is_toplevel) {
1516 _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1517 }
1518 }
1519
GGDKDrawRaise(GWindow w)1520 static void GGDKDrawRaise(GWindow w) {
1521 GGDKWindow gw = (GGDKWindow) w;
1522 Log(LOGDEBUG, "%p[%p][%s]", gw, gw->w, gw->window_title);
1523 if (!gw->is_visible) {
1524 Log(LOGINFO, "Discarding raise on hidden window: %p[%p][%s]",
1525 gw, gw->w, gw->window_title);
1526 return;
1527 }
1528
1529 _GGDKDraw_CleanupAutoPaint(gw->display);
1530 gdk_window_raise(gw->w);
1531 if (!w->is_toplevel) {
1532 _GGDKDraw_FakeConfigureEvent(gw);
1533 }
1534 }
1535
GGDKDrawRaiseAbove(GWindow gw1,GWindow gw2)1536 static void GGDKDrawRaiseAbove(GWindow gw1, GWindow gw2) {
1537 Log(LOGDEBUG, " ");
1538 _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw1)->display);
1539 gdk_window_restack(((GGDKWindow)gw1)->w, ((GGDKWindow)gw2)->w, true);
1540 if (!gw1->is_toplevel) {
1541 _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw1);
1542 }
1543 if (!gw2->is_toplevel) {
1544 _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw2);
1545 }
1546 }
1547
1548 // Only used once in gcontainer - force it to call GDrawRaiseAbove
GGDKDrawIsAbove(GWindow UNUSED (gw1),GWindow UNUSED (gw2))1549 static int GGDKDrawIsAbove(GWindow UNUSED(gw1), GWindow UNUSED(gw2)) {
1550 Log(LOGDEBUG, " ");
1551 return false;
1552 }
1553
GGDKDrawLower(GWindow gw)1554 static void GGDKDrawLower(GWindow gw) {
1555 Log(LOGDEBUG, " ");
1556 _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1557 gdk_window_lower(((GGDKWindow)gw)->w);
1558 if (!gw->is_toplevel) {
1559 _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1560 }
1561 }
1562
1563 // Icon title is ignored.
GGDKDrawSetWindowTitles8(GWindow w,const char * title,const char * UNUSED (icontitle))1564 static void GGDKDrawSetWindowTitles8(GWindow w, const char *title, const char *UNUSED(icontitle)) {
1565 Log(LOGDEBUG, " ");// assert(false);
1566 GGDKWindow gw = (GGDKWindow)w;
1567 free(gw->window_title);
1568 gw->window_title = copy(title);
1569
1570 if (title != NULL && gw->is_toplevel) {
1571 gdk_window_set_title(gw->w, title);
1572 }
1573 }
1574
GGDKDrawSetWindowTitles(GWindow gw,const unichar_t * title,const unichar_t * UNUSED (icontitle))1575 static void GGDKDrawSetWindowTitles(GWindow gw, const unichar_t *title, const unichar_t *UNUSED(icontitle)) {
1576 Log(LOGDEBUG, " ");
1577 char *str = u2utf8_copy(title);
1578 if (str != NULL) {
1579 GGDKDrawSetWindowTitles8(gw, str, NULL);
1580 free(str);
1581 }
1582 }
1583
1584 // Sigh. GDK doesn't provide a way to get the window title...
GGDKDrawGetWindowTitle(GWindow gw)1585 static unichar_t *GGDKDrawGetWindowTitle(GWindow gw) {
1586 Log(LOGDEBUG, " "); // assert(false);
1587 return utf82u_copy(((GGDKWindow)gw)->window_title);
1588 }
1589
GGDKDrawGetWindowTitle8(GWindow gw)1590 static char *GGDKDrawGetWindowTitle8(GWindow gw) {
1591 Log(LOGDEBUG, " ");
1592 return copy(((GGDKWindow)gw)->window_title);
1593 }
1594
GGDKDrawSetTransientFor(GWindow transient,GWindow owner)1595 static void GGDKDrawSetTransientFor(GWindow transient, GWindow owner) {
1596 Log(LOGDEBUG, "transient=%p, owner=%p", transient, owner);
1597 GGDKWindow gw = (GGDKWindow) transient, ow = NULL;
1598 GGDKDisplay *gdisp = gw->display;
1599 assert(owner == NULL || gw->is_toplevel);
1600
1601 if (!gw->is_toplevel) {
1602 return; // Transient child windows?!
1603 }
1604
1605 if (owner == (GWindow) - 1) {
1606 for (GList_Glib *pw = gdisp->mru_windows->head; pw != NULL; pw = pw->next) {
1607 GGDKWindow tw = (GGDKWindow)pw->data;
1608 if (tw != gw && tw->is_visible) {
1609 ow = tw;
1610 break;
1611 }
1612 }
1613 } else if (owner == NULL) {
1614 ow = NULL; // Does this work with GDK?
1615 } else {
1616 ow = (GGDKWindow)owner;
1617 }
1618
1619 if (gw->transient_owner != NULL) {
1620 for (int i = ((int)gdisp->transient_stack->len) - 1; i >= 0; --i) {
1621 if (gw == (GGDKWindow)gdisp->transient_stack->pdata[i]) {
1622 g_ptr_array_remove_index(gw->display->transient_stack, i);
1623 if (gw->restrict_input_to_me) {
1624 gdisp->restrict_count--;
1625 }
1626 break;
1627 }
1628 }
1629 }
1630
1631 if (ow != NULL) {
1632 gdk_window_set_transient_for(gw->w, ow->w);
1633 gdk_window_set_modal_hint(gw->w, true);
1634 gw->istransient = true;
1635 g_ptr_array_add(gdisp->transient_stack, gw);
1636 if (gw->restrict_input_to_me) {
1637 gdisp->restrict_count++;
1638 }
1639 } else {
1640 gdk_window_set_modal_hint(gw->w, false);
1641 gdk_window_set_transient_for(gw->w, gw->display->groot->w);
1642 gw->istransient = false;
1643 }
1644
1645 gw->transient_owner = ow;
1646 }
1647
GGDKDrawGetPointerPosition(GWindow w,GEvent * ret)1648 static void GGDKDrawGetPointerPosition(GWindow w, GEvent *ret) {
1649 Log(LOGDEBUG, " ");
1650 GGDKWindow gw = (GGDKWindow)w;
1651 GdkModifierType mask;
1652 int x, y;
1653
1654 GdkDevice *pointer = _GGDKDraw_GetPointer(gw->display);
1655 if (pointer == NULL) {
1656 ret->u.mouse.x = 0;
1657 ret->u.mouse.y = 0;
1658 ret->u.mouse.state = 0;
1659 return;
1660 }
1661
1662 gdk_window_get_device_position(gw->w, pointer, &x, &y, &mask);
1663 ret->u.mouse.x = x;
1664 ret->u.mouse.y = y;
1665 ret->u.mouse.state = _GGDKDraw_GdkModifierToKsm(mask);
1666 }
1667
GGDKDrawGetPointerWindow(GWindow gw)1668 static GWindow GGDKDrawGetPointerWindow(GWindow gw) {
1669 Log(LOGDEBUG, " ");
1670 GdkDevice *pointer = _GGDKDraw_GetPointer(((GGDKWindow)gw)->display);
1671 GdkWindow *window = gdk_device_get_window_at_position(pointer, NULL, NULL);
1672
1673 if (window != NULL) {
1674 return (GWindow)g_object_get_data(G_OBJECT(window), "GGDKWindow");
1675 }
1676 return NULL;
1677 }
1678
GGDKDrawSetCursor(GWindow w,GCursor gcursor)1679 static void GGDKDrawSetCursor(GWindow w, GCursor gcursor) {
1680 Log(LOGDEBUG, " ");
1681 GGDKWindow gw = (GGDKWindow)w;
1682 GdkCursor *cursor = NULL;
1683
1684 switch (gcursor) {
1685 case ct_default:
1686 case ct_backpointer:
1687 case ct_pointer:
1688 cursor = gdk_cursor_new_from_name(gw->display->display, "default");
1689 break;
1690 case ct_hand:
1691 cursor = gdk_cursor_new_from_name(gw->display->display, "hand");
1692 break;
1693 case ct_question:
1694 cursor = gdk_cursor_new_from_name(gw->display->display, "help");
1695 break;
1696 case ct_cross:
1697 cursor = gdk_cursor_new_from_name(gw->display->display, "crosshair");
1698 break;
1699 case ct_4way:
1700 cursor = gdk_cursor_new_from_name(gw->display->display, "move");
1701 break;
1702 case ct_text:
1703 cursor = gdk_cursor_new_from_name(gw->display->display, "text");
1704 break;
1705 case ct_watch:
1706 cursor = gdk_cursor_new_from_name(gw->display->display, "wait");
1707 break;
1708 case ct_draganddrop:
1709 cursor = gdk_cursor_new_from_name(gw->display->display, "pointer");
1710 break;
1711 case ct_invisible:
1712 return; // There is no *good* reason to make the cursor invisible
1713 break;
1714 default:
1715 Log(LOGDEBUG, "CUSTOM CURSOR! %d", gcursor);
1716 }
1717
1718 if (gcursor >= ct_user) {
1719 GGDKDisplay *gdisp = gw->display;
1720 gcursor -= ct_user;
1721 if (gcursor < gdisp->cursors->len && gdisp->cursors->pdata[gcursor] != NULL) {
1722 gdk_window_set_cursor(gw->w, (GdkCursor *)gdisp->cursors->pdata[gcursor]);
1723 gw->current_cursor = gcursor;
1724 } else {
1725 Log(LOGWARN, "Invalid cursor value passed: %d", gcursor);
1726 }
1727 } else {
1728 gdk_window_set_cursor(gw->w, cursor);
1729 gw->current_cursor = gcursor;
1730 if (cursor != NULL) {
1731 g_object_unref(cursor);
1732 }
1733 }
1734 }
1735
GGDKDrawGetCursor(GWindow gw)1736 static GCursor GGDKDrawGetCursor(GWindow gw) {
1737 Log(LOGDEBUG, " ");
1738 return ((GGDKWindow)gw)->current_cursor;
1739 }
1740
GGDKDrawGetRedirectWindow(GDisplay * UNUSED (gdisp))1741 static GWindow GGDKDrawGetRedirectWindow(GDisplay *UNUSED(gdisp)) {
1742 //Log(LOGDEBUG, " ");
1743 // Not implemented.
1744 return NULL;
1745 }
1746
GGDKDrawTranslateCoordinates(GWindow from,GWindow to,GPoint * pt)1747 static void GGDKDrawTranslateCoordinates(GWindow from, GWindow to, GPoint *pt) {
1748 //Log(LOGDEBUG, " ");
1749 GGDKWindow gfrom = (GGDKWindow)from, gto = (GGDKWindow)to;
1750 GGDKDisplay *gdisp = gfrom->display;
1751
1752
1753 if (gto == gdisp->groot) {
1754 // The actual meaning of this command...
1755 int x, y;
1756 gdk_window_get_root_coords(gfrom->w, pt->x, pt->y, &x, &y);
1757 pt->x = x;
1758 pt->y = y;
1759 } else {
1760 GdkPoint from_root, to_root;
1761 gdk_window_get_origin(gfrom->w, &from_root.x, &from_root.y);
1762 gdk_window_get_origin(gto->w, &to_root.x, &to_root.y);
1763 pt->x = from_root.x - to_root.x + pt->x;
1764 pt->y = from_root.y - to_root.y + pt->y;
1765 }
1766 }
1767
1768
GGDKDrawBeep(GDisplay * gdisp)1769 static void GGDKDrawBeep(GDisplay *gdisp) {
1770 //Log(LOGDEBUG, " ");
1771 gdk_display_beep(((GGDKDisplay *)gdisp)->display);
1772 }
1773
GGDKDrawScroll(GWindow w,GRect * rect,int32 hor,int32 vert)1774 static void GGDKDrawScroll(GWindow w, GRect *rect, int32 hor, int32 vert) {
1775 //Log(LOGDEBUG, " ");
1776 GGDKWindow gw = (GGDKWindow) w;
1777 GRect temp;
1778
1779 vert = -vert;
1780 if (rect == NULL) {
1781 temp.x = temp.y = 0;
1782 temp.width = gw->pos.width;
1783 temp.height = gw->pos.height;
1784 rect = &temp;
1785 }
1786
1787 GDrawRequestExpose(w, rect, false);
1788 }
1789
1790
GGDKDrawCreateInputContext(GWindow UNUSED (gw),enum gic_style UNUSED (style))1791 static GIC *GGDKDrawCreateInputContext(GWindow UNUSED(gw), enum gic_style UNUSED(style)) {
1792 Log(LOGDEBUG, " ");
1793 return NULL;
1794 }
1795
GGDKDrawSetGIC(GWindow UNUSED (gw),GIC * UNUSED (gic),int UNUSED (x),int UNUSED (y))1796 static void GGDKDrawSetGIC(GWindow UNUSED(gw), GIC *UNUSED(gic), int UNUSED(x), int UNUSED(y)) {
1797 Log(LOGDEBUG, " ");
1798 }
1799
GGDKDrawKeyState(GWindow w,int keysym)1800 static int GGDKDrawKeyState(GWindow w, int keysym) {
1801 //Log(LOGDEBUG, " ");
1802 if (keysym != ' ') {
1803 Log(LOGWARN, "Cannot check state of unsupported character!");
1804 return 0;
1805 }
1806 // Since this function is only used to check the state of the space button
1807 // Dont't bother with a full implementation...
1808 return ((GGDKWindow)w)->display->is_space_pressed;
1809 }
1810
GGDKDrawGrabSelection(GWindow w,enum selnames sn)1811 static void GGDKDrawGrabSelection(GWindow w, enum selnames sn) {
1812 Log(LOGDEBUG, " ");
1813
1814 if ((int)sn < 0 || sn >= sn_max) {
1815 return;
1816 }
1817
1818 GGDKWindow gw = (GGDKWindow)w;
1819 GGDKDisplay *gdisp = gw->display;
1820 GGDKSelectionInfo *sel = &gdisp->selinfo[sn];
1821
1822 // Send a SelectionClear event to the current owner of the selection
1823 if (sel->owner != NULL && sel->datalist != NULL) {
1824 GEvent e = {0};
1825 e.w = (GWindow)sel->owner;
1826 e.type = et_selclear;
1827 e.u.selclear.sel = sn;
1828 e.native_window = sel->owner->w;
1829 if (sel->owner->eh != NULL) {
1830 _GGDKDraw_CallEHChecked(sel->owner, &e, sel->owner->eh);
1831 }
1832 }
1833 _GGDKDraw_ClearSelData(gdisp, sn);
1834
1835 gdk_selection_owner_set(gw->w, sel->sel_atom, gdisp->last_event_time, false);
1836 sel->owner = gw;
1837 sel->timestamp = gdisp->last_event_time;
1838 }
1839
GGDKDrawAddSelectionType(GWindow w,enum selnames sel,char * type,void * data,int32 cnt,int32 unitsize,void * gendata (void *,int32 * len),void freedata (void *))1840 static void GGDKDrawAddSelectionType(GWindow w, enum selnames sel, char *type, void *data, int32 cnt, int32 unitsize,
1841 void *gendata(void *, int32 *len), void freedata(void *)) {
1842 Log(LOGDEBUG, " ");
1843
1844 GGDKWindow gw = (GGDKWindow)w;
1845 GGDKDisplay *gdisp = gw->display;
1846 GdkAtom type_atom = gdk_atom_intern(type, false);
1847 GGDKSelectionData *sd = NULL;
1848 GList_Glib *dl = gdisp->selinfo[sel].datalist;
1849
1850 if (unitsize != 1 && unitsize != 2 && unitsize != 4) {
1851 GDrawIError("Bad unitsize to GGDKDrawAddSelectionType");
1852 return;
1853 } else if ((int)sel < 0 || sel >= sn_max) {
1854 GDrawIError("Bad selname value");
1855 return;
1856 }
1857
1858 while (dl != NULL && (sd = ((GGDKSelectionData *)dl->data))->type_atom != type_atom) {
1859 dl = dl->next;
1860 }
1861
1862 if (dl == NULL) {
1863 sd = calloc(1, sizeof(GGDKSelectionData));
1864 sd->type_atom = type_atom;
1865 gdisp->selinfo[sel].datalist = g_list_append(gdisp->selinfo[sel].datalist, sd);
1866 }
1867
1868 sd->cnt = cnt;
1869 sd->data = data;
1870 sd->unit_size = unitsize;
1871 sd->gendata = gendata;
1872 sd->freedata = freedata;
1873
1874 #ifdef GDK_WINDOWING_QUARTZ
1875 if (sel == sn_clipboard && type_atom == gdk_atom_intern_static_string("UTF8_STRING")) {
1876 char *data = sd->data;
1877 if (sd->gendata) {
1878 int len;
1879 data = sd->gendata(sd->data, &len);
1880 }
1881 _GGDKDrawCocoa_SetClipboardText(data);
1882 if (sd->gendata) {
1883 free(data);
1884 }
1885 }
1886 #endif
1887 }
1888
GGDKDrawRequestSelection(GWindow w,enum selnames sn,char * typename,int32 * len)1889 static void *GGDKDrawRequestSelection(GWindow w, enum selnames sn, char *typename, int32 *len) {
1890 GGDKWindow gw = (GGDKWindow)w;
1891 GGDKDisplay *gdisp = gw->display;
1892 GdkAtom type_atom = gdk_atom_intern(typename, false);
1893 void *ret = NULL;
1894
1895 if (len != NULL) {
1896 *len = 0;
1897 }
1898
1899 if ((int)sn < 0 || sn >= sn_max || gw->is_waiting_for_selection || gw->is_dying) {
1900 return NULL;
1901 }
1902
1903 #ifdef GDK_WINDOWING_QUARTZ
1904 if (sn == sn_clipboard && type_atom == gdk_atom_intern_static_string("UTF8_STRING")) {
1905 ret = _GGDKDrawCocoa_GetClipboardText();
1906 if (ret && len) {
1907 *len = strlen(ret);
1908 }
1909 return ret;
1910 }
1911 #endif
1912
1913 // If we own the selection, get the data ourselves...
1914 if (gdisp->selinfo[sn].owner != NULL) {
1915 GList_Glib *ptr = gdisp->selinfo[sn].datalist;
1916 while (ptr != NULL) {
1917 GGDKSelectionData *sd = (GGDKSelectionData *)ptr->data;
1918 if (sd->type_atom == type_atom) {
1919 if (sd->gendata != NULL) {
1920 ret = (sd->gendata)(sd->data, len);
1921 if (len != NULL) {
1922 *len *= sd->unit_size;
1923 }
1924 } else {
1925 int sz = sd->unit_size * sd->cnt;
1926 ret = calloc(sz + 4, 1);
1927 if (ret) {
1928 memcpy(ret, sd->data, sz);
1929 if (len != NULL) {
1930 *len = sz;
1931 }
1932 }
1933 }
1934 return ret;
1935 }
1936 ptr = ptr->next;
1937 }
1938 }
1939
1940 #ifndef GDK_WINDOWING_QUARTZ
1941 // Otherwise we have to ask the owner for the data.
1942 gdk_selection_convert(gw->w, gdisp->selinfo[sn].sel_atom, type_atom, gdisp->last_event_time);
1943
1944 // On Windows, gdk_selection_convert is synchronous. Avoid the fluff of waiting
1945 // which also can cause segfault if a window gets destroyed in the meantime...
1946 #ifdef GDK_WINDOWING_X11
1947 gw->is_waiting_for_selection = true;
1948 gw->is_notified_of_selection = false;
1949
1950 GTimer_GTK *timer = g_timer_new();
1951 g_timer_start(timer);
1952
1953 while (gw->is_waiting_for_selection) {
1954 if (g_timer_elapsed(timer, NULL) >= (double)gdisp->sel_notify_timeout) {
1955 break;
1956 }
1957 GGDKDrawProcessPendingEvents((GDisplay *)gdisp);
1958 }
1959 g_timer_destroy(timer);
1960
1961 if (gw->is_notified_of_selection) {
1962 #endif
1963 guchar *data;
1964 GdkAtom received_type;
1965 gint received_format;
1966 gint rlen = gdk_selection_property_get(gw->w, &data, &received_type, &received_format);
1967 if (data != NULL) {
1968 ret = calloc(rlen + 4, 1);
1969 if (ret) {
1970 memcpy(ret, data, rlen);
1971 if (len) {
1972 *len = rlen;
1973 }
1974 }
1975 g_free(data);
1976 }
1977 #ifdef GDK_WINDOWING_X11
1978 }
1979
1980 gw->is_waiting_for_selection = false;
1981 gw->is_notified_of_selection = false;
1982 #endif // GDK_WINDOWING_X11
1983 #endif // GDK_WINDOWING_QUARTZ
1984
1985 return ret;
1986 }
1987
GGDKDrawSelectionHasType(GWindow w,enum selnames sn,char * typename)1988 static int GGDKDrawSelectionHasType(GWindow w, enum selnames sn, char *typename) {
1989 Log(LOGDEBUG, " ");
1990
1991 GGDKWindow gw = (GGDKWindow)w;
1992 if (gw->is_dying || gw->is_waiting_for_selection || (int)sn < 0 || sn >= sn_max) {
1993 return false;
1994 }
1995
1996 GGDKDisplay *gdisp = gw->display;
1997 GdkAtom sel_type = gdk_atom_intern(typename, false);
1998
1999 // Check if we own it
2000 if (gdisp->selinfo[sn].owner != NULL) {
2001 GList_Glib *ptr = gdisp->selinfo[sn].datalist;
2002 while (ptr != NULL) {
2003 if (((GGDKSelectionData *)ptr->data)->type_atom == sel_type) {
2004 return true;
2005 }
2006 ptr = ptr->next;
2007 }
2008 return false;
2009 }
2010
2011 #ifndef GDK_WINDOWING_QUARTZ
2012 // Else query
2013 if (gdisp->seltypes.timestamp != gdisp->last_event_time || gdisp->seltypes.sel_atom != gdisp->selinfo[sn].sel_atom) {
2014 free(gdisp->seltypes.types);
2015 gdisp->seltypes.types = NULL;
2016 gdisp->seltypes.sel_atom = gdisp->selinfo[sn].sel_atom;
2017 gdisp->seltypes.types = (GdkAtom *) GGDKDrawRequestSelection(w, sn, "TARGETS", &gdisp->seltypes.len);
2018 gdisp->seltypes.len /= sizeof(GdkAtom *);
2019 }
2020
2021 if (gdisp->seltypes.types != NULL) {
2022 for (int i = 0; i < gdisp->seltypes.len; i++) {
2023 if (gdisp->seltypes.types[i] == sel_type) {
2024 return true;
2025 }
2026 }
2027 }
2028 return false;
2029 #else
2030 return sel_type == gdk_atom_intern_static_string("UTF8_STRING");
2031 #endif
2032 }
2033
GGDKDrawBindSelection(GDisplay * disp,enum selnames sn,char * atomname)2034 static void GGDKDrawBindSelection(GDisplay *disp, enum selnames sn, char *atomname) {
2035 Log(LOGDEBUG, " ");
2036 GGDKDisplay *gdisp = (GGDKDisplay *) disp;
2037 if ((int)sn >= 0 && sn < sn_max) {
2038 gdisp->selinfo[sn].sel_atom = gdk_atom_intern(atomname, false);
2039 }
2040 }
2041
GGDKDrawSelectionHasOwner(GDisplay * disp,enum selnames sn)2042 static int GGDKDrawSelectionHasOwner(GDisplay *disp, enum selnames sn) {
2043 Log(LOGDEBUG, " ");
2044
2045 if ((int)sn < 0 || sn >= sn_max) {
2046 return false;
2047 }
2048
2049 GGDKDisplay *gdisp = (GGDKDisplay *)disp;
2050 // Check if we own it
2051 if (gdisp->selinfo[sn].owner != NULL) {
2052 return true;
2053 }
2054
2055 // Else query. Note, we cannot use gdk_selection_owner_get, as this only works for
2056 // windows that GDK created. This code is untested.
2057 void *data = GGDKDrawRequestSelection((GWindow)gdisp->groot, sn, "TIMESTAMP", NULL);
2058 if (data != NULL) {
2059 free(data);
2060 return true;
2061 }
2062 return false;
2063 }
2064
GGDKDrawPointerUngrab(GDisplay * gdisp)2065 static void GGDKDrawPointerUngrab(GDisplay *gdisp) {
2066 Log(LOGDEBUG, " ");
2067 #ifndef GGDKDRAW_GDK_3_20
2068 GdkDevice *pointer = _GGDKDraw_GetPointer((GGDKDisplay *)gdisp);
2069 if (pointer == NULL) {
2070 return;
2071 }
2072
2073 gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
2074 #else
2075 GdkSeat *seat = gdk_display_get_default_seat(((GGDKDisplay *)gdisp)->display);
2076 if (seat == NULL) {
2077 return;
2078 }
2079
2080 gdk_seat_ungrab(seat);
2081 #endif
2082 }
2083
GGDKDrawPointerGrab(GWindow w)2084 static void GGDKDrawPointerGrab(GWindow w) {
2085 Log(LOGDEBUG, " ");
2086 GGDKWindow gw = (GGDKWindow)w;
2087 #ifndef GGDKDRAW_GDK_3_20
2088 GdkDevice *pointer = _GGDKDraw_GetPointer(gw->display);
2089 if (pointer == NULL) {
2090 return;
2091 }
2092
2093 gdk_device_grab(pointer, gw->w,
2094 GDK_OWNERSHIP_NONE,
2095 false,
2096 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK,
2097 NULL, GDK_CURRENT_TIME);
2098 #else
2099 GdkSeat *seat = gdk_display_get_default_seat(gw->display->display);
2100 if (seat == NULL) {
2101 return;
2102 }
2103
2104 gdk_seat_grab(seat, gw->w,
2105 GDK_SEAT_CAPABILITY_ALL_POINTING,
2106 false, NULL, NULL, NULL, NULL);
2107 #endif
2108 }
2109
GGDKDrawRequestExpose(GWindow w,GRect * rect,int UNUSED (doclear))2110 static void GGDKDrawRequestExpose(GWindow w, GRect *rect, int UNUSED(doclear)) {
2111 Log(LOGDEBUG, "%p [%s]", w, ((GGDKWindow)w)->window_title);
2112
2113 GGDKWindow gw = (GGDKWindow) w;
2114 GdkRectangle clip;
2115
2116 if (!gw->is_visible || _GGDKDraw_WindowOrParentsDying(gw)) {
2117 return;
2118 }
2119 if (rect == NULL) {
2120 clip.x = clip.y = 0;
2121 clip.width = gw->pos.width;
2122 clip.height = gw->pos.height;
2123 } else {
2124 clip.x = rect->x;
2125 clip.y = rect->y;
2126 clip.width = rect->width;
2127 clip.height = rect->height;
2128
2129 if (rect->x < 0 || rect->y < 0 || rect->x + rect->width > gw->pos.width ||
2130 rect->y + rect->height > gw->pos.height) {
2131
2132 if (clip.x < 0) {
2133 clip.width += clip.x;
2134 clip.x = 0;
2135 }
2136 if (clip.y < 0) {
2137 clip.height += clip.y;
2138 clip.y = 0;
2139 }
2140 if (clip.x + clip.width > gw->pos.width) {
2141 clip.width = gw->pos.width - clip.x;
2142 }
2143 if (clip.y + clip.height > gw->pos.height) {
2144 clip.height = gw->pos.height - clip.y;
2145 }
2146 if (clip.height <= 0 || clip.width <= 0) {
2147 return;
2148 }
2149 }
2150 }
2151
2152 gdk_window_invalidate_rect(gw->w, &clip, false);
2153 }
2154
GGDKDrawForceUpdate(GWindow gw)2155 static void GGDKDrawForceUpdate(GWindow gw) {
2156 //Log(LOGDEBUG, " ");
2157 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2158 gdk_window_process_updates(((GGDKWindow)gw)->w, true);
2159 G_GNUC_END_IGNORE_DEPRECATIONS
2160 }
2161
GGDKDrawSync(GDisplay * gdisp)2162 static void GGDKDrawSync(GDisplay *gdisp) {
2163 //Log(LOGDEBUG, " ");
2164 gdk_display_sync(((GGDKDisplay *)gdisp)->display);
2165 }
2166
GGDKDrawSkipMouseMoveEvents(GWindow UNUSED (gw),GEvent * UNUSED (gevent))2167 static void GGDKDrawSkipMouseMoveEvents(GWindow UNUSED(gw), GEvent *UNUSED(gevent)) {
2168 //Log(LOGDEBUG, " ");
2169 // Not implemented, not needed.
2170 }
2171
GGDKDrawProcessPendingEvents(GDisplay * gdisp)2172 static void GGDKDrawProcessPendingEvents(GDisplay *gdisp) {
2173 //Log(LOGDEBUG, " ");
2174 GMainContext *ctx = g_main_loop_get_context(((GGDKDisplay *)gdisp)->main_loop);
2175 if (ctx != NULL) {
2176 _GGDKDraw_CleanupAutoPaint((GGDKDisplay *)gdisp);
2177 while (g_main_context_iteration(ctx, false));
2178 }
2179 }
2180
GGDKDrawProcessWindowEvents(GWindow w)2181 static void GGDKDrawProcessWindowEvents(GWindow w) {
2182 Log(LOGWARN, "This function SHOULD NOT BE CALLED! Will likely not do as expected! Window: %p", w);
2183
2184 if (w != NULL) {
2185 GGDKDrawProcessPendingEvents(w->display);
2186 }
2187 }
2188
GGDKDrawProcessOneEvent(GDisplay * gdisp)2189 static void GGDKDrawProcessOneEvent(GDisplay *gdisp) {
2190 //Log(LOGDEBUG, " ");
2191 GMainContext *ctx = g_main_loop_get_context(((GGDKDisplay *)gdisp)->main_loop);
2192 if (ctx != NULL) {
2193 _GGDKDraw_CleanupAutoPaint((GGDKDisplay *)gdisp);
2194 g_main_context_iteration(ctx, true);
2195 }
2196 }
2197
GGDKDrawEventLoop(GDisplay * gdisp)2198 static void GGDKDrawEventLoop(GDisplay *gdisp) {
2199 Log(LOGDEBUG, " ");
2200 GMainContext *ctx = g_main_loop_get_context(((GGDKDisplay *)gdisp)->main_loop);
2201 if (ctx != NULL) {
2202 _GGDKDraw_CleanupAutoPaint((GGDKDisplay *)gdisp);
2203 do {
2204 while (((GGDKDisplay *)gdisp)->top_window_count > 0) {
2205 g_main_context_iteration(ctx, true);
2206 if ((gdisp->err_flag) && (gdisp->err_report)) {
2207 GDrawIErrorRun("%s", gdisp->err_report);
2208 }
2209 if (gdisp->err_report) {
2210 free(gdisp->err_report);
2211 gdisp->err_report = NULL;
2212 }
2213 }
2214 GGDKDrawSync(gdisp);
2215 GGDKDrawProcessPendingEvents(gdisp);
2216 GGDKDrawSync(gdisp);
2217 } while (((GGDKDisplay *)gdisp)->top_window_count > 0 || g_main_context_pending(ctx));
2218 }
2219 }
2220
GGDKDrawPostEvent(GEvent * e)2221 static void GGDKDrawPostEvent(GEvent *e) {
2222 //Log(LOGDEBUG, " ");
2223 GGDKWindow gw = (GGDKWindow)(e->w);
2224 e->native_window = gw->w;
2225 _GGDKDraw_CallEHChecked(gw, e, gw->eh);
2226 }
2227
GGDKDrawPostDragEvent(GWindow w,GEvent * mouse,enum event_type et)2228 static void GGDKDrawPostDragEvent(GWindow w, GEvent *mouse, enum event_type et) {
2229 Log(LOGDEBUG, " ");
2230 GGDKWindow gw = (GGDKWindow)w, cw;
2231 GGDKDisplay *gdisp = gw->display;
2232 int x, y;
2233
2234 // If the cursor hasn't moved much, don't bother to send a drag event
2235 x = abs(mouse->u.mouse.x - gdisp->last_dd.x);
2236 y = abs(mouse->u.mouse.y - gdisp->last_dd.y);
2237 if (x + y < 4 && et == et_drag) {
2238 return;
2239 }
2240
2241 cw = (GGDKWindow)GGDKDrawGetPointerWindow(w);
2242
2243 if (gdisp->last_dd.w != cw) {
2244 if (gdisp->last_dd.w != NULL) {
2245 GEvent e = {0};
2246 e.type = et_dragout;
2247 e.u.drag_drop.x = gdisp->last_dd.rx;
2248 e.u.drag_drop.y = gdisp->last_dd.ry;
2249 e.w = (GWindow)gdisp->last_dd.w;
2250 e.native_window = e.w->native_window;
2251
2252 _GGDKDraw_CallEHChecked(gdisp->last_dd.w, &e, gdisp->last_dd.w->eh);
2253 } else {
2254 /* Send foreign dragout message */
2255 }
2256 gdisp->last_dd.w = NULL;
2257 }
2258
2259 GEvent e = {0};
2260 // Are we still within the original window?
2261 if (cw == gw) {
2262 e.type = et;
2263 e.w = w;
2264 e.native_window = w->native_window;
2265 x = e.u.drag_drop.x = mouse->u.mouse.x;
2266 y = e.u.drag_drop.y = mouse->u.mouse.y;
2267 _GGDKDraw_CallEHChecked(gw, &e, gw->eh);
2268 } else if (cw != NULL) {
2269 GPoint pt = {.x = mouse->u.mouse.x, .y = mouse->u.mouse.y};
2270 GGDKDrawTranslateCoordinates(w, (GWindow)cw, &pt);
2271 x = pt.x;
2272 y = pt.y;
2273
2274 e.type = et;
2275 e.u.drag_drop.x = x;
2276 e.u.drag_drop.y = y;
2277 e.w = (GWindow)cw;
2278 e.native_window = cw->w;
2279 _GGDKDraw_CallEHChecked(cw, &e, cw->eh);
2280 } else {
2281 // Foreign window
2282 }
2283
2284 if (et != et_drop) {
2285 gdisp->last_dd.w = cw;
2286 gdisp->last_dd.x = mouse->u.mouse.x;
2287 gdisp->last_dd.y = mouse->u.mouse.y;
2288 gdisp->last_dd.rx = x;
2289 gdisp->last_dd.ry = y;
2290 } else {
2291 gdisp->last_dd.w = NULL;
2292 }
2293 }
2294
GGDKDrawRequestDeviceEvents(GWindow w,int devcnt,struct gdeveventmask * de)2295 static int GGDKDrawRequestDeviceEvents(GWindow w, int devcnt, struct gdeveventmask *de) {
2296 Log(LOGDEBUG, " ");
2297 return 0; //Not sure how to handle... For tablets...
2298 }
2299
GGDKDrawRequestTimer(GWindow w,int32 time_from_now,int32 frequency,void * userdata)2300 static GTimer *GGDKDrawRequestTimer(GWindow w, int32 time_from_now, int32 frequency, void *userdata) {
2301 //Log(LOGDEBUG, " ");
2302 GGDKTimer *timer = calloc(1, sizeof(GGDKTimer));
2303 GGDKWindow gw = (GGDKWindow)w;
2304 if (timer == NULL) {
2305 return NULL;
2306 }
2307
2308 gw->display->timers = g_list_append(gw->display->timers, timer);
2309
2310 timer->owner = w;
2311 timer->repeat_time = frequency;
2312 timer->userdata = userdata;
2313 timer->active = true;
2314 timer->has_differing_repeat_time = (time_from_now != frequency);
2315 timer->glib_timeout_id = g_timeout_add(time_from_now, _GGDKDraw_ProcessTimerEvent, timer);
2316
2317 GGDKDRAW_ADDREF(timer);
2318 return (GTimer *)timer;
2319 }
2320
GGDKDrawCancelTimer(GTimer * timer)2321 static void GGDKDrawCancelTimer(GTimer *timer) {
2322 //Log(LOGDEBUG, " ");
2323 GGDKTimer *gtimer = (GGDKTimer *)timer;
2324 gtimer->active = false;
2325 GGDKDRAW_DECREF(gtimer, _GGDKDraw_OnTimerDestroyed);
2326 }
2327
2328 // Our function VTable
2329 static struct displayfuncs gdkfuncs = {
2330 GGDKDrawInit,
2331
2332 GGDKDrawSetDefaultIcon,
2333
2334 GGDKDrawCreateTopWindow,
2335 GGDKDrawCreateSubWindow,
2336 GGDKDrawCreatePixmap,
2337 GGDKDrawCreateBitmap,
2338 GGDKDrawCreateCursor,
2339 GGDKDrawDestroyWindow,
2340 GGDKDrawDestroyCursor,
2341 GGDKDrawNativeWindowExists, //Not sure what this is meant to do...
2342 GGDKDrawSetZoom,
2343 GGDKDrawSetWindowBackground,
2344 GGDKDrawSetDither,
2345
2346 GGDKDrawSetVisible,
2347 GGDKDrawMove,
2348 GGDKDrawTrueMove,
2349 GGDKDrawResize,
2350 GGDKDrawMoveResize,
2351 GGDKDrawRaise,
2352 GGDKDrawRaiseAbove,
2353 GGDKDrawIsAbove,
2354 GGDKDrawLower,
2355 GGDKDrawSetWindowTitles,
2356 GGDKDrawSetWindowTitles8,
2357 GGDKDrawGetWindowTitle,
2358 GGDKDrawGetWindowTitle8,
2359 GGDKDrawSetTransientFor,
2360 GGDKDrawGetPointerPosition,
2361 GGDKDrawGetPointerWindow,
2362 GGDKDrawSetCursor,
2363 GGDKDrawGetCursor,
2364 GGDKDrawGetRedirectWindow,
2365 GGDKDrawTranslateCoordinates,
2366
2367 GGDKDrawBeep,
2368
2369 GGDKDrawPushClip,
2370 GGDKDrawPopClip,
2371
2372 GGDKDrawSetDifferenceMode,
2373
2374 GGDKDrawClear,
2375 GGDKDrawDrawLine,
2376 GGDKDrawDrawArrow,
2377 GGDKDrawDrawRect,
2378 GGDKDrawFillRect,
2379 GGDKDrawFillRoundRect,
2380 GGDKDrawDrawEllipse,
2381 GGDKDrawFillEllipse,
2382 GGDKDrawDrawArc,
2383 GGDKDrawDrawPoly,
2384 GGDKDrawFillPoly,
2385 GGDKDrawScroll,
2386
2387 GGDKDrawDrawImage,
2388 GGDKDrawDrawGlyph,
2389 GGDKDrawDrawImageMagnified,
2390 GGDKDrawDrawPixmap,
2391
2392 GGDKDrawCreateInputContext,
2393 GGDKDrawSetGIC,
2394 GGDKDrawKeyState,
2395
2396 GGDKDrawGrabSelection,
2397 GGDKDrawAddSelectionType,
2398 GGDKDrawRequestSelection,
2399 GGDKDrawSelectionHasType,
2400 GGDKDrawBindSelection,
2401 GGDKDrawSelectionHasOwner,
2402
2403 GGDKDrawPointerUngrab,
2404 GGDKDrawPointerGrab,
2405 GGDKDrawRequestExpose,
2406 GGDKDrawForceUpdate,
2407 GGDKDrawSync,
2408 GGDKDrawSkipMouseMoveEvents,
2409 GGDKDrawProcessPendingEvents,
2410 GGDKDrawProcessWindowEvents,
2411 GGDKDrawProcessOneEvent,
2412 GGDKDrawEventLoop,
2413 GGDKDrawPostEvent,
2414 GGDKDrawPostDragEvent,
2415 GGDKDrawRequestDeviceEvents,
2416
2417 GGDKDrawRequestTimer,
2418 GGDKDrawCancelTimer,
2419
2420 GGDKDrawGetFontMetrics,
2421
2422 GGDKDrawHasCairo,
2423 GGDKDrawPathStartNew,
2424 GGDKDrawPathClose,
2425 GGDKDrawPathMoveTo,
2426 GGDKDrawPathLineTo,
2427 GGDKDrawPathCurveTo,
2428 GGDKDrawPathStroke,
2429 GGDKDrawPathFill,
2430 GGDKDrawPathFillAndStroke, // Currently unused
2431
2432 GGDKDrawLayoutInit,
2433 GGDKDrawLayoutDraw,
2434 GGDKDrawLayoutIndexToPos,
2435 GGDKDrawLayoutXYToIndex,
2436 GGDKDrawLayoutExtents,
2437 GGDKDrawLayoutSetWidth,
2438 GGDKDrawLayoutLineCount,
2439 GGDKDrawLayoutLineStart,
2440 GGDKDrawStartNewSubPath,
2441 GGDKDrawFillRuleSetWinding,
2442
2443 GGDKDrawDoText8,
2444
2445 GGDKDrawPushClipOnly,
2446 GGDKDrawClipPreserve
2447 };
2448
2449 // Protected member functions (package-level)
2450
_GGDKDraw_CreateDisplay(char * displayname,char * UNUSED (programname))2451 GDisplay *_GGDKDraw_CreateDisplay(char *displayname, char *UNUSED(programname)) {
2452 GGDKDisplay *gdisp;
2453 GdkDisplay *display;
2454 GGDKWindow groot;
2455
2456 LogInit();
2457
2458 if (displayname == NULL) {
2459 display = gdk_display_get_default();
2460 } else {
2461 display = gdk_display_open(displayname);
2462 }
2463
2464 if (display == NULL) {
2465 return NULL;
2466 }
2467
2468 gdisp = (GGDKDisplay *)calloc(1, sizeof(GGDKDisplay));
2469 if (gdisp == NULL) {
2470 return NULL;
2471 }
2472
2473 // cursors.c creates ~41.
2474 gdisp->cursors = g_ptr_array_sized_new(50);
2475 gdisp->windows = g_hash_table_new(g_direct_hash, g_direct_equal);
2476 gdisp->mru_windows = g_queue_new();
2477 if (gdisp->windows == NULL || gdisp->mru_windows == NULL) {
2478 if (gdisp->windows) {
2479 g_hash_table_destroy(gdisp->windows);
2480 }
2481 free(gdisp);
2482 return NULL;
2483 }
2484 gdisp->transient_stack = g_ptr_array_sized_new(5);
2485
2486 gdisp->funcs = &gdkfuncs;
2487 gdisp->display = display;
2488 gdisp->screen = gdk_display_get_default_screen(display);
2489 gdisp->root = gdk_screen_get_root_window(gdisp->screen);
2490 gdisp->res = gdk_screen_get_resolution(gdisp->screen);
2491 gdisp->pangoc_context = gdk_pango_context_get_for_screen(gdisp->screen);
2492 if (gdisp->res <= 0) {
2493 gdisp->res = 96;
2494 }
2495 #ifdef GDK_WINDOWING_QUARTZ
2496 if (gdisp->res <= 72) {
2497 gdisp->res = 96;
2498 }
2499 #endif
2500
2501 gdisp->main_loop = g_main_loop_new(NULL, true);
2502 gdisp->bs.double_time = 200;
2503 gdisp->bs.double_wiggle = 3;
2504 gdisp->sel_notify_timeout = 2; // 2 second timeout
2505
2506 //Initialise selection atoms
2507 gdisp->selinfo[sn_primary].sel_atom = gdk_atom_intern_static_string("PRIMARY");
2508 gdisp->selinfo[sn_clipboard].sel_atom = gdk_atom_intern_static_string("CLIPBOARD");
2509 gdisp->selinfo[sn_drag_and_drop].sel_atom = gdk_atom_intern_static_string("DRAG_AND_DROP");
2510 gdisp->selinfo[sn_user1].sel_atom = gdk_atom_intern_static_string("PRIMARY");
2511 gdisp->selinfo[sn_user2].sel_atom = gdk_atom_intern_static_string("PRIMARY");
2512
2513 bool tbf = false, mxc = false;
2514 int user_res = 0;
2515 GResStruct res[] = {
2516 {.resname = "MultiClickTime", .type = rt_int, .val = &gdisp->bs.double_time},
2517 {.resname = "MultiClickWiggle", .type = rt_int, .val = &gdisp->bs.double_wiggle},
2518 {.resname = "SelectionNotifyTimeout", .type = rt_int, .val = &gdisp->sel_notify_timeout},
2519 {.resname = "TwoButtonFixup", .type = rt_bool, .val = &tbf},
2520 {.resname = "MacOSXCmd", .type = rt_bool, .val = &mxc},
2521 {.resname = "ScreenResolution", .type = rt_int, .val = &user_res},
2522 {.resname = NULL},
2523 };
2524 GResourceFind(res, NULL);
2525 gdisp->twobmouse_win = tbf;
2526 gdisp->macosx_cmd = mxc;
2527
2528 // Now finalise the resolution
2529 if (user_res > 0) {
2530 gdisp->res = user_res;
2531 }
2532 pango_cairo_context_set_resolution(gdisp->pangoc_context, gdisp->res);
2533
2534 groot = (GGDKWindow)calloc(1, sizeof(struct ggdkwindow));
2535 if (groot == NULL) {
2536 g_object_unref(gdisp->pangoc_context);
2537 g_queue_free(gdisp->mru_windows);
2538 g_hash_table_destroy(gdisp->windows);
2539 free(gdisp);
2540 return NULL;
2541 }
2542
2543 gdisp->groot = groot;
2544 groot->ggc = _GGDKDraw_NewGGC();
2545 groot->display = gdisp;
2546 groot->w = gdisp->root;
2547 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2548 groot->pos.width = gdk_screen_get_width(gdisp->screen);
2549 groot->pos.height = gdk_screen_get_height(gdisp->screen);
2550 G_GNUC_END_IGNORE_DEPRECATIONS
2551 groot->is_toplevel = true;
2552 groot->is_visible = true;
2553 g_object_set_data(G_OBJECT(gdisp->root), "GGDKWindow", groot);
2554
2555 gdisp->def_background = GResourceFindColor("Background", COLOR_CREATE(0xf5, 0xff, 0xfa));
2556 gdisp->def_foreground = GResourceFindColor("Foreground", COLOR_CREATE(0x00, 0x00, 0x00));
2557 if (GResourceFindBool("Synchronize", false)) {
2558 gdk_display_sync(gdisp->display);
2559 }
2560
2561 (gdisp->funcs->init)((GDisplay *) gdisp);
2562
2563 gdk_event_handler_set(_GGDKDraw_DispatchEvent, (gpointer)gdisp, NULL);
2564
2565 _GDraw_InitError((GDisplay *) gdisp);
2566
2567 //DEBUG
2568 if (getenv("GGDK_DEBUG")) {
2569 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2570 gdk_window_set_debug_updates(true);
2571 G_GNUC_END_IGNORE_DEPRECATIONS
2572 }
2573 return (GDisplay *)gdisp;
2574 }
2575
_GGDKDraw_DestroyDisplay(GDisplay * disp)2576 void _GGDKDraw_DestroyDisplay(GDisplay *disp) {
2577 GGDKDisplay *gdisp = (GGDKDisplay *)(disp);
2578 guint window_count = g_hash_table_size(gdisp->windows);
2579
2580 // Indicate we're dying...
2581 gdisp->is_dying = true;
2582
2583 // Destroy remaining windows
2584 if (window_count > 0) {
2585 Log(LOGINFO, "Windows left allocated - forcibly freeing!");
2586
2587 // Note that windows are destroyed in random order. When a window is
2588 // destroyed, it decrements its parent reference count, so we need a
2589 // large enough count to keep it around until we're ready to destroy it
2590 GHashTableIter iter;
2591 GGDKWindow gw;
2592 g_hash_table_iter_init(&iter, gdisp->windows);
2593 while (g_hash_table_iter_next(&iter, (void **)&gw, NULL)) {
2594 gw->reference_count = window_count + 2;
2595 }
2596 while (g_hash_table_size(gdisp->windows) > 0) {
2597 g_hash_table_iter_init(&iter, gdisp->windows);
2598 if (g_hash_table_iter_next(&iter, (void **)&gw, NULL)) {
2599 Log(LOGINFO, "Forcibly destroying window (%p:%s)", gw, gw->window_title);
2600 assert(gw->reference_count >= 2);
2601 GGDKDrawDestroyWindow((GWindow)gw);
2602 _GGDKDraw_OnWindowDestroyed(gw);
2603 }
2604 }
2605 }
2606
2607 // Destroy root window
2608 _GGDKDraw_OnWindowDestroyed(gdisp->groot);
2609 gdisp->groot = NULL;
2610
2611 // Destroy the mru window list
2612 g_queue_free(gdisp->mru_windows);
2613 gdisp->mru_windows = NULL;
2614
2615 // Finally destroy the window table
2616 g_hash_table_destroy(gdisp->windows);
2617 gdisp->windows = NULL;
2618
2619 g_ptr_array_free(gdisp->transient_stack, true);
2620
2621 // Destroy held selection types, if any
2622 free(gdisp->seltypes.types);
2623 gdisp->seltypes.types = NULL;
2624
2625 // Destroy cursors
2626 for (guint i = 0; i < gdisp->cursors->len; i++) {
2627 if (gdisp->cursors->pdata[i] != NULL) {
2628 GGDKDrawDestroyCursor((GDisplay *)gdisp, (GCursor)(ct_user + i));
2629 }
2630 }
2631 g_ptr_array_free(gdisp->cursors, true);
2632 gdisp->cursors = NULL;
2633
2634 // Destroy any orphaned timers (???)
2635 if (gdisp->timers != NULL) {
2636 Log(LOGWARN, "Orphaned timers present - forcibly freeing!");
2637 while (gdisp->timers != NULL) {
2638 GGDKTimer *timer = (GGDKTimer *)gdisp->timers->data;
2639 timer->reference_count = 1;
2640 GGDKDrawCancelTimer((GTimer *)timer);
2641 }
2642 }
2643
2644 // Get rid of our pango context
2645 g_object_unref(gdisp->pangoc_context);
2646 gdisp->pangoc_context = NULL;
2647
2648 // Destroy the fontstate
2649 free(gdisp->fontstate);
2650 gdisp->fontstate = NULL;
2651
2652 // Close the display
2653 if (gdisp->display != NULL) {
2654 gdisp->display = NULL;
2655 }
2656
2657 // Unreference the main loop
2658 g_main_loop_unref(gdisp->main_loop);
2659 gdisp->main_loop = NULL;
2660
2661 // Free the data structure
2662 free(gdisp);
2663 return;
2664 }
2665
2666 #endif // FONTFORGE_CAN_USE_GDK
2667