1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include <X11/keysym.h>
4 #include <xlib/ui_xim.h> /* ui_xim_display_opened */
5 #include <gdk/gdkx.h>
6 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
7 #include <X11/extensions/XInput2.h>
8 #endif
9
10 #if GTK_CHECK_VERSION(2, 90, 0)
11 #define gdk_x11_drawable_get_xid(window) gdk_x11_window_get_xid(window)
12 #endif
13
14 #if 0
15 /* Forcibly enable transparency on gnome-terminal which doesn't support it. */
16 #define FORCE_TRANSPARENCY
17 #endif
18
19 /* --- static variables --- */
20
21 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
22 static int is_xinput2;
23 #endif
24
25 /* --- static functions --- */
26
toplevel_configure(gpointer data)27 static gboolean toplevel_configure(gpointer data) {
28 VteTerminal *terminal = data;
29
30 if (PVT(terminal)->screen->window.is_transparent) {
31 XEvent ev;
32
33 if (!XCheckTypedWindowEvent(disp.display, gdk_x11_drawable_get_xid(gtk_widget_get_window(
34 gtk_widget_get_toplevel(GTK_WIDGET(terminal)))),
35 ConfigureNotify, &ev)) {
36 ui_window_set_transparent(&PVT(terminal)->screen->window,
37 ui_screen_get_picture_modifier(PVT(terminal)->screen));
38 } else {
39 XPutBackEvent(disp.display, &ev);
40 }
41 }
42
43 return FALSE;
44 }
45
46 static void vte_terminal_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
47
48 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
xi2_get_state(XIModifierState * mods_state,XIButtonState * buttons_state,XIGroupState * group_state)49 static u_int xi2_get_state (XIModifierState *mods_state,
50 XIButtonState *buttons_state,
51 XIGroupState *group_state) {
52 u_int state = 0;
53
54 if (mods_state) {
55 state = mods_state->effective;
56 }
57
58 if (buttons_state) {
59 int count;
60
61 /*
62 * 5 is always less than buttons_state->mask_len * 8,
63 * so skip to check if 5 <= buttons_state->mask_len * 8.
64 */
65 for (count = 1; count <= 5; count++) {
66 if (XIMaskIsSet (buttons_state->mask, count)) {
67 state |= (Button1Mask << (count - 1));
68 }
69 }
70 }
71
72 if (group_state) {
73 state |= (group_state->effective) << 13;
74 }
75
76 return state;
77 }
78
xievent_to_xevent(XIDeviceEvent * xiev,XEvent * xev)79 static int xievent_to_xevent(XIDeviceEvent *xiev, XEvent *xev) {
80 switch (xiev->evtype) {
81 case XI_KeyPress:
82 xev->xkey.type = KeyPress;
83 xev->xkey.window = xiev->event;
84 xev->xkey.root = xiev->root;
85 xev->xkey.subwindow = xiev->child;
86 xev->xkey.time = xiev->time;
87 xev->xkey.x = xiev->event_x;
88 xev->xkey.y = xiev->event_y;
89 xev->xkey.x_root = xiev->root_x;
90 xev->xkey.y_root = xiev->root_y;
91 xev->xkey.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
92 xev->xkey.keycode = xiev->detail;
93 xev->xkey.same_screen = True;
94 break;
95
96 case XI_ButtonPress:
97 xev->xbutton.type = ButtonPress;
98 xev->xbutton.window = xiev->event;
99 xev->xbutton.root = xiev->root;
100 xev->xbutton.subwindow = xiev->child;
101 xev->xbutton.time = xiev->time;
102 xev->xbutton.x = xiev->event_x;
103 xev->xbutton.y = xiev->event_y;
104 xev->xbutton.x_root = xiev->root_x;
105 xev->xbutton.y_root = xiev->root_y;
106 xev->xbutton.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
107 xev->xbutton.button = xiev->detail;
108 xev->xbutton.same_screen = True;
109 break;
110
111 case XI_ButtonRelease:
112 xev->xbutton.type = ButtonRelease;
113 xev->xbutton.window = xiev->event;
114 xev->xbutton.root = xiev->root;
115 xev->xbutton.subwindow = xiev->child;
116 xev->xbutton.time = xiev->time;
117 xev->xbutton.x = xiev->event_x;
118 xev->xbutton.y = xiev->event_y;
119 xev->xbutton.x_root = xiev->root_x;
120 xev->xbutton.y_root = xiev->root_y;
121 xev->xbutton.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
122 xev->xbutton.button = xiev->detail;
123 xev->xbutton.same_screen = True;
124 break;
125
126 case XI_Motion:
127 xev->xmotion.type = MotionNotify;
128 xev->xmotion.window = xiev->event;
129 xev->xmotion.root = xiev->root;
130 xev->xmotion.subwindow = xiev->child;
131 xev->xmotion.time = xiev->time;
132 xev->xmotion.x = xiev->event_x;
133 xev->xmotion.y = xiev->event_y;
134 xev->xmotion.x_root = xiev->root_x;
135 xev->xmotion.y_root = xiev->root_y;
136 xev->xmotion.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
137 xev->xmotion.is_hint = 0;
138 xev->xmotion.same_screen = True;
139 break;
140
141 #if 0
142 case XI_FocusIn:
143 xev->xfocus.type = FocusIn;
144 xev->xfocus.window = ((XIEnterEvent*)xiev)->event;
145 xev->xfocus.mode = ((XIEnterEvent*)xiev)->mode;
146 xev->xfocus.detail = ((XIEnterEvent*)xiev)->detail;
147 break;
148
149 case XI_FocusOut:
150 xev->xfocus.type = FocusOut;
151 xev->xfocus.window = ((XIEnterEvent*)xiev)->event;
152 xev->xfocus.mode = ((XIEnterEvent*)xiev)->mode;
153 xev->xfocus.detail = ((XIEnterEvent*)xiev)->detail;
154 break;
155 #endif
156
157 default:
158 return 0;
159 }
160
161 #if 0
162 bl_debug_printf("%s devid %d srcid %d window %d child %d: "
163 "rootx %0.f rooty %0.f x %0.f y %0.f detail %d serial %d\n",
164 xiev->evtype == XI_KeyPress ? "KeyPress" :
165 (xiev->evtype == XI_ButtonPress ? "ButtonPress" :
166 (xiev->evtype == XI_ButtonRelease ? "ButtonRelease" :
167 (xiev->evtype == XI_Motion ? "Motion" : "Unknown"))),
168 xiev->deviceid, xiev->sourceid, xiev->event, xiev->child,
169 xiev->root_x, xiev->root_y, xiev->event_x, xiev->event_y,
170 xiev->detail, xev->xany.serial);
171 #endif
172
173 return 1;
174 }
175 #endif
176
177 /*
178 * Don't call vt_close_dead_terms() before returning GDK_FILTER_CONTINUE,
179 * because vt_close_dead_terms() will destroy widget in pty_closed and
180 * destroyed widget can be touched right after this function.
181 */
vte_terminal_filter(GdkXEvent * xevent,GdkEvent * event,gpointer data)182 static GdkFilterReturn vte_terminal_filter(GdkXEvent *xevent, GdkEvent *event, /* GDK_NOTHING */
183 gpointer data) {
184 u_int count;
185 int is_key_event;
186 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
187 XEvent new_xev;
188 XGenericEventCookie *cookie;
189 #endif
190
191 if (XFilterEvent((XEvent *)xevent, None)) {
192 return GDK_FILTER_REMOVE;
193 }
194
195 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
196 if (((XEvent *)xevent)->type == GenericEvent) {
197 #if 0
198 static int xi_opcode;
199
200 if (xi_opcode == 0) {
201 int xi_error;
202 int xi_event;
203
204 if(!XQueryExtension(disp.display, "XInputExtension", &xi_opcode, &xi_event, &xi_error)) {
205 return GDK_FILTER_CONTINUE;
206 }
207 }
208 #endif
209
210 cookie = &((XEvent*)xevent)->xcookie;
211
212 #if 0
213 if (cookie->extension != xi_opcode) {
214 return GDK_FILTER_CONTINUE;
215 }
216 #endif
217
218 memcpy(&new_xev, xevent, sizeof(XGenericEventCookie));
219 if (!xievent_to_xevent(cookie->data, &new_xev)) {
220 return GDK_FILTER_CONTINUE;
221 }
222 xevent = &new_xev;
223 }
224 #endif
225
226 if ((((XEvent *)xevent)->type == KeyPress || ((XEvent *)xevent)->type == KeyRelease)) {
227 is_key_event = 1;
228 } else {
229 is_key_event = 0;
230 }
231
232 for (count = 0; count < disp.num_roots; count++) {
233 VteTerminal *terminal;
234
235 if (IS_MLTERM_SCREEN(disp.roots[count])) {
236 terminal = VTE_WIDGET((ui_screen_t *)disp.roots[count]);
237
238 if (!PVT(terminal)->term) {
239 /* pty is already closed and new pty is not attached yet. */
240 continue;
241 }
242
243 /*
244 * Key events are ignored if window isn't focused.
245 * This processing is added for key binding of popup menu.
246 */
247 if (is_key_event) {
248 if (((XEvent *)xevent)->xany.window == disp.roots[count]->my_window) {
249 if (PVT(terminal)->screen->copymode == NULL) {
250 vt_term_search_reset_position(PVT(terminal)->term);
251 }
252
253 if (!disp.roots[count]->is_focused) {
254 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
255 if (xevent == &new_xev) {
256 ((XIDeviceEvent*)cookie->data)->event =
257 gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
258 } else
259 #endif
260 {
261 ((XEvent *)xevent)->xany.window =
262 gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
263 }
264
265 return GDK_FILTER_CONTINUE;
266 }
267 }
268 } else if (PVT(terminal)->screen->window.is_transparent &&
269 ((XEvent *)xevent)->type == ConfigureNotify &&
270 ((XEvent *)xevent)->xconfigure.event ==
271 gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)))) {
272 /*
273 * If terminal position is changed by adding menu bar or tab,
274 * transparent background is reset.
275 */
276
277 gint x;
278 gint y;
279
280 gdk_window_get_position(gtk_widget_get_window(GTK_WIDGET(terminal)), &x, &y);
281
282 /*
283 * XXX
284 * I don't know why but the height of menu bar has been already
285 * added to the position of terminal before first
286 * GdkConfigureEvent whose x and y is 0 is received.
287 * But (x != xconfigure.x || y != xconfigure.y) is true eventually
288 * and ui_window_set_transparent() is called expectedly.
289 */
290 if (x != ((XEvent *)xevent)->xconfigure.x || y != ((XEvent *)xevent)->xconfigure.y) {
291 ui_window_set_transparent(&PVT(terminal)->screen->window,
292 ui_screen_get_picture_modifier(PVT(terminal)->screen));
293 }
294
295 return GDK_FILTER_CONTINUE;
296 }
297 } else {
298 terminal = NULL;
299 }
300
301 if (ui_window_receive_event(disp.roots[count], (XEvent *)xevent)) {
302 static pid_t config_menu_pid = 0;
303
304 if (!terminal || /* SCIM etc window */
305 /* XFilterEvent in ui_window_receive_event. */
306 ((XEvent *)xevent)->xany.window != disp.roots[count]->my_window) {
307 return GDK_FILTER_REMOVE;
308 }
309
310 /* XXX Hack for waiting for config menu program exiting. */
311 if (PVT(terminal)->term->pty &&
312 config_menu_pid != PVT(terminal)->term->pty->config_menu.pid) {
313 if ((config_menu_pid = PVT(terminal)->term->pty->config_menu.pid)) {
314 vte_reaper_add_child(config_menu_pid);
315 }
316 }
317
318 if (is_key_event || ((XEvent *)xevent)->type == ButtonPress ||
319 ((XEvent *)xevent)->type == ButtonRelease) {
320 /* Hook key and button events for popup menu. */
321 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
322 if (xevent == &new_xev) {
323 ((XIDeviceEvent*)cookie->data)->event =
324 gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
325 } else
326 #endif
327 {
328 ((XEvent *)xevent)->xany.window =
329 gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
330 }
331
332 return GDK_FILTER_CONTINUE;
333 } else {
334 return GDK_FILTER_REMOVE;
335 }
336 }
337 /*
338 * xconfigure.window: window whose size, position, border, and/or stacking
339 * order was changed.
340 * => processed in following.
341 * xconfigure.event: reconfigured window or to its parent.
342 * (=XAnyEvent.window) => processed in ui_window_receive_event()
343 */
344 else if (/* terminal && */ ((XEvent *)xevent)->type == ConfigureNotify &&
345 ((XEvent *)xevent)->xconfigure.window == disp.roots[count]->my_window) {
346 #if 0
347 /*
348 * This check causes resize problem in opening tab in
349 * gnome-terminal(2.29.6).
350 */
351 if (((XEvent *)xevent)->xconfigure.width != GTK_WIDGET(terminal)->allocation.width ||
352 ((XEvent *)xevent)->xconfigure.height != GTK_WIDGET(terminal)->allocation.height)
353 #else
354 if (CHAR_WIDTH(terminal) != ui_col_width(PVT(terminal)->screen) ||
355 CHAR_HEIGHT(terminal) != ui_line_height(PVT(terminal)->screen))
356 #endif
357 {
358 /* Window was changed due to change of font size inside mlterm. */
359 GtkAllocation alloc;
360
361 gtk_widget_get_allocation(GTK_WIDGET(terminal), &alloc);
362 alloc.width = ((XEvent *)xevent)->xconfigure.width;
363 alloc.height = ((XEvent *)xevent)->xconfigure.height;
364
365 #ifdef __DEBUG
366 bl_debug_printf(BL_DEBUG_TAG " child is resized\n");
367 #endif
368
369 vte_terminal_size_allocate(GTK_WIDGET(terminal), &alloc);
370 }
371
372 return GDK_FILTER_REMOVE;
373 }
374 }
375
376 return GDK_FILTER_CONTINUE;
377 }
378
379 #ifdef FORCE_TRANSPARENCY
380 #if VTE_CHECK_VERSION(0, 38, 0)
set_rgba_visual(GtkWidget * widget)381 static void set_rgba_visual(GtkWidget *widget) {
382 GdkScreen *screen;
383
384 if ((screen = gtk_widget_get_screen(widget)) && gdk_screen_is_composited(screen)) {
385 gtk_widget_set_visual(widget, gdk_screen_get_rgba_visual(screen));
386 }
387 }
388
toplevel_draw(GtkWidget * widget,cairo_t * cr,void * user_data)389 static gboolean toplevel_draw(GtkWidget *widget, cairo_t *cr, void *user_data) {
390 GtkWidget *child = user_data;
391
392 gint width = gtk_widget_get_allocated_width(child);
393 gint height = gtk_widget_get_allocated_height(child);
394 gint x;
395 gint y;
396
397 gtk_widget_translate_coordinates(child, widget, 0, 0, &x, &x);
398 cairo_rectangle(cr, x, y, width, height);
399 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
400 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
401 cairo_fill(cr);
402
403 return FALSE;
404 }
405 #endif
406 #endif
407
vte_terminal_hierarchy_changed(GtkWidget * widget,GtkWidget * old_toplevel,gpointer data)408 static void vte_terminal_hierarchy_changed(GtkWidget *widget, GtkWidget *old_toplevel,
409 gpointer data) {
410 GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
411
412 if (old_toplevel) {
413 g_signal_handlers_disconnect_by_func(old_toplevel, toplevel_configure, widget);
414 }
415
416 g_signal_connect_swapped(toplevel, "configure-event",
417 G_CALLBACK(toplevel_configure), VTE_TERMINAL(widget));
418
419 #ifdef FORCE_TRANSPARENCY
420 #if VTE_CHECK_VERSION(0, 38, 0)
421 /*
422 * Though vte 0.38.0 or later doesn't support rgba visual,
423 * this forcibly enables it.
424 */
425 set_rgba_visual(toplevel);
426
427 /* roxterm calls gtk_widget_set_app_paintable(TRUE) internally. */
428 if (!gtk_widget_get_app_paintable(toplevel)) {
429 /*
430 * XXX
431 * gtk_widget_set_app_paintable(TRUE) enables transparency, but unexpectedly
432 * makes a menu bar of gnome-terminal(3.24.2) transparent, sigh...
433 */
434 gtk_widget_set_app_paintable(toplevel, TRUE);
435 g_signal_connect(toplevel, "draw", G_CALLBACK(toplevel_draw), widget);
436
437 if (old_toplevel) {
438 g_signal_handlers_disconnect_by_func(old_toplevel, toplevel_draw, widget);
439 }
440 }
441 #endif
442 #endif
443 }
444
show_root(ui_display_t * disp,GtkWidget * widget)445 static void show_root(ui_display_t *disp, GtkWidget *widget) {
446 VteTerminal *terminal = VTE_TERMINAL(widget);
447 XID xid = gdk_x11_drawable_get_xid(gtk_widget_get_window(widget));
448
449 if (disp->gc->gc == DefaultGC(disp->display, disp->screen)) {
450 /*
451 * Replace visual, colormap, depth and gc with those inherited from parent
452 * xid.
453 * In some cases that those of parent xid is not DefaultVisual,
454 * DefaultColormap
455 * and so on (e.g. compiz), BadMatch error can happen.
456 */
457
458 XWindowAttributes attr;
459 XGCValues gc_value;
460 int depth_is_changed;
461
462 XGetWindowAttributes(disp->display, xid, &attr);
463 disp->visual = attr.visual;
464 disp->colormap = attr.colormap;
465 depth_is_changed = (disp->depth != attr.depth);
466 disp->depth = attr.depth;
467
468 /* ui_gc_t using DefaultGC is already created in vte_terminal_class_init */
469 gc_value.foreground = disp->gc->fg_color;
470 gc_value.background = disp->gc->bg_color;
471 gc_value.graphics_exposures = True;
472 disp->gc->gc =
473 XCreateGC(disp->display, xid, GCForeground | GCBackground | GCGraphicsExposures, &gc_value);
474
475 #ifdef __DEBUG
476 bl_debug_printf(BL_DEBUG_TAG " Visual %x Colormap %x Depth %d\n", disp->visual, disp->colormap,
477 disp->depth);
478 #endif
479
480 if (depth_is_changed &&
481 /* see ui_screen_new() */
482 !PVT(terminal)->screen->window.is_transparent &&
483 !PVT(terminal)->screen->pic_file_path) {
484 ui_change_true_transbg_alpha(PVT(terminal)->screen->color_man, main_config.alpha);
485 ui_color_manager_reload(PVT(terminal)->screen->color_man);
486
487 /* No colors are cached for now. */
488 #if 0
489 ui_color_cache_unload_all();
490 #endif
491 }
492 }
493
494 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
495 if (is_xinput2) {
496 ui_window_remove_event_mask(&PVT(terminal)->screen->window,
497 ButtonPressMask | ButtonMotionMask | ButtonReleaseMask |
498 KeyPressMask);
499 }
500 #endif
501
502 ui_display_show_root(disp, &PVT(terminal)->screen->window, 0, 0, 0, "mlterm", xid);
503
504 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
505 if (is_xinput2) {
506 XIEventMask mask;
507
508 /* If XIAllDevices is set and 'a' key is pressed, 'aa' can be output. */
509 mask.deviceid = XIAllMasterDevices;
510 mask.mask_len = XIMaskLen(XI_LASTEVENT);
511 mask.mask = calloc(mask.mask_len, 1);
512 XISetMask(mask.mask, XI_Motion);
513 XISetMask(mask.mask, XI_ButtonPress);
514 XISetMask(mask.mask, XI_ButtonRelease);
515 XISetMask(mask.mask, XI_KeyPress);
516 #if 0
517 XISetMask(mask.mask, XI_KeyRelease);
518 XISetMask(mask.mask, XI_FocusIn);
519 XISetMask(mask.mask, XI_FocusOut);
520 #endif
521 XISelectEvents(disp->display, PVT(terminal)->screen->window.my_window, &mask, 1);
522 XSync(disp->display, False);
523 free(mask.mask);
524 }
525 #endif
526 }
527
init_display(ui_display_t * disp,VteTerminalClass * vclass)528 static void init_display(ui_display_t *disp, VteTerminalClass *vclass) {
529 GdkDisplay *gdkdisp = gdk_display_get_default();
530 const char *name = gdk_display_get_name(gdkdisp);
531 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
532 GdkDeviceManager *devman = gdk_display_get_device_manager(gdkdisp);
533
534 if (G_OBJECT_TYPE(devman) == GDK_TYPE_X11_DEVICE_MANAGER_XI2) {
535 is_xinput2 = 1;
536 }
537 #endif
538
539 if (name && strstr(name, "wayland")) {
540 GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
541 GTK_BUTTONS_CLOSE,
542 "%s display is not supported on xlib", name);
543 gtk_dialog_run(GTK_DIALOG(dialog));
544 gtk_widget_destroy(dialog);
545 exit(1);
546 }
547
548 disp->display = gdk_x11_display_get_xdisplay(gdkdisp);
549 disp->screen = DefaultScreen(disp->display);
550 disp->my_window = DefaultRootWindow(disp->display);
551 disp->visual = DefaultVisual(disp->display, disp->screen);
552 disp->colormap = DefaultColormap(disp->display, disp->screen);
553 disp->depth = DefaultDepth(disp->display, disp->screen);
554 disp->gc = ui_gc_new(disp->display, None);
555 disp->width = DisplayWidth(disp->display, disp->screen);
556 disp->height = DisplayHeight(disp->display, disp->screen);
557 disp->modmap.serial = 0;
558 disp->modmap.map = XGetModifierMapping(disp->display);
559
560 ui_xim_display_opened(disp->display);
561
562 gdk_window_add_filter(NULL, vte_terminal_filter, NULL);
563 }
564