1 /***********************************************************/
2 /* */
3 /* System dependent application management (unix, x11) */
4 /* */
5 /***********************************************************/
6
7 #include "apricot.h"
8 #include "unix/guts.h"
9 #include "Application.h"
10 #include <sys/utsname.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <signal.h>
14 #if !defined(BYTEORDER)
15 #error "BYTEORDER is not defined"
16 #endif
17 #define LSB32 0x1234
18 #define LSB64 0x12345678
19 #define MSB32 0x4321
20 #define MSB64 0x87654321
21 #ifndef BUFSIZ
22 #define BUFSIZ 2048
23 #endif
24
25 UnixGuts guts, *pguts = &guts;
26 static XErrorEvent *save_xerror_event = NULL;
27
28 UnixGuts *
prima_unix_guts(void)29 prima_unix_guts(void)
30 {
31 return &guts;
32 }
33
34 static int
x_error_handler(Display * d,XErrorEvent * ev)35 x_error_handler( Display *d, XErrorEvent *ev)
36 {
37 int tail = guts. ri_tail;
38 int prev = tail;
39 char *name = "Prima";
40 char buf[BUFSIZ];
41 char mesg[BUFSIZ];
42 char number[32];
43
44 if ( save_xerror_event ) {
45 *save_xerror_event = *ev;
46 save_xerror_event = NULL;
47 return 0;
48 }
49
50 while ( tail != guts. ri_head) {
51 if ( guts. ri[ tail]. request > ev-> serial)
52 break;
53 prev = tail;
54 tail++;
55 if ( tail >= REQUEST_RING_SIZE)
56 tail = 0;
57 }
58
59 switch ( ev-> request_code) {
60 case 38: /* X_QueryPointer - apc_event uses sequence of XQueryPointer calls,
61 to find where the pointer belongs. The error is raised when one
62 of the windows disappears . */
63 case 42: /* X_SetInputFocus */
64 return 0;
65 }
66
67 #ifdef NEED_X11_EXTENSIONS_XRENDER_H
68 if ( ev-> request_code == guts. xft_xrender_major_opcode &&
69 ev-> request_code > 127 &&
70 ev-> error_code == BadLength)
71 /* Xrender large polygon request failed */
72 guts. xft_disable_large_fonts = 1;
73 #endif
74
75 #ifdef HAVE_X11_EXTENSIONS_XCOMPOSITE_H
76 if ( ev-> request_code == guts. composite_opcode &&
77 ev->minor_code == X_CompositeRedirectSubwindows) {
78 /* checking for composite manager */
79 guts. composite_error_triggered = true;
80 return 0;
81 }
82 #endif
83
84 XGetErrorText( d, ev-> error_code, buf, BUFSIZ);
85 XGetErrorDatabaseText( d, name, "XError", "X Error", mesg, BUFSIZ);
86 fprintf( stderr, "%s: %s, request: %d", mesg, buf, ev->request_code);
87 if ( ev->request_code < 128) {
88 sprintf( number, "%d", ev->request_code);
89 XGetErrorDatabaseText( d, "XRequest", number, "", buf, BUFSIZ);
90 fprintf( stderr, "(%s)", buf);
91 }
92 if ( tail == guts. ri_head && prev == guts. ri_head);
93 else if ( tail == guts. ri_head)
94 fprintf( stderr, ", after %s:%d\n",
95 guts. ri[ prev]. file, guts. ri[ prev]. line);
96 else
97 fprintf( stderr, ", between %s:%d and %s:%d\n",
98 guts. ri[ prev]. file, guts. ri[ prev]. line,
99 guts. ri[ tail]. file, guts. ri[ tail]. line);
100 return 0;
101 }
102
103 void
prima_save_xerror_event(XErrorEvent * xr)104 prima_save_xerror_event( XErrorEvent *xr)
105 {
106 bzero( xr, sizeof(XErrorEvent));
107 save_xerror_event = xr;
108 }
109
110 void
prima_restore_xerror_event(XErrorEvent * xr)111 prima_restore_xerror_event( XErrorEvent *xr)
112 {
113 save_xerror_event = NULL;
114 if ( xr && xr->display != NULL) x_error_handler( xr-> display, xr);
115 }
116
117
118 static int
x_io_error_handler(Display * d)119 x_io_error_handler( Display *d)
120 {
121 fprintf( stderr, "Fatal input/output X error\n");
122 _exit( 1);
123 return 0; /* happy now? */
124 }
125
126 static XrmDatabase
get_database(void)127 get_database( void)
128 {
129 XrmDatabase db = XrmGetStringDatabase( "");
130 char filename[PATH_MAX];
131 char *c;
132 char *resource_data = XResourceManagerString( DISP);
133 if ( resource_data) {
134 XrmCombineDatabase( XrmGetStringDatabase( resource_data), &db, false);
135 } else {
136 c = getenv( "HOME");
137 if (!c) c = "";
138 snprintf( filename, PATH_MAX, "%s/.Xdefaults", c);
139 XrmCombineFileDatabase( filename, &db, false);
140 }
141 return db;
142 }
143
144 static int
get_idepth(void)145 get_idepth( void)
146 {
147 int i, n;
148 XPixmapFormatValues *format = XListPixmapFormats( DISP, &n);
149 int idepth = guts.depth;
150
151 if ( !format) return guts.depth;
152
153 for ( i = 0; i < n; i++)
154 if ( format[i]. depth == guts. depth) {
155 idepth = format[i]. bits_per_pixel;
156 break;
157 }
158 XFree( format);
159 return idepth;
160 }
161
162 static Bool do_x11 = true;
163 static Bool do_sync = false;
164 static char* do_display = NULL;
165 static int do_debug = 0;
166 static Bool do_icccm_only = false;
167 static Bool do_no_shmem = false;
168 static Bool do_no_gtk = false;
169 static Bool do_no_quartz = false;
170 static Bool do_no_xrender = false;
171
172 static Bool
init_x11(char * error_buf)173 init_x11( char * error_buf )
174 {
175 /*XXX*/ /* Namely, support for -display host:0.0 etc. */
176 XrmQuark common_quarks_list[20]; /*XXX change number of elements if necessary */
177 XrmQuarkList ql = common_quarks_list;
178 XGCValues gcv;
179 char *common_quarks =
180 "String."
181 "Blinkinvisibletime.blinkinvisibletime."
182 "Blinkvisibletime.blinkvisibletime."
183 "Clicktimeframe.clicktimeframe."
184 "Doubleclicktimeframe.doubleclicktimeframe."
185 "Wheeldown.wheeldown."
186 "Wheelup.wheelup."
187 "Submenudelay.submenudelay."
188 "Scrollfirst.scrollfirst."
189 "Scrollnext.scrollnext";
190
191 char * atom_names[AI_count] = {
192 "RESOLUTION_X",
193 "RESOLUTION_Y",
194 "PIXEL_SIZE",
195 "SPACING",
196 "RELATIVE_WEIGHT",
197 "FOUNDRY",
198 "AVERAGE_WIDTH",
199 "CHARSET_REGISTRY",
200 "CHARSET_ENCODING",
201 "CREATE_EVENT",
202 "WM_DELETE_WINDOW",
203 "WM_PROTOCOLS",
204 "WM_TAKE_FOCUS",
205 "_NET_WM_STATE",
206 "_NET_WM_STATE_SKIP_TASKBAR",
207 "_NET_WM_STATE_MAXIMIZED_VERT",
208 "_NET_WM_STATE_MAXIMIZED_HORZ",
209 "_NET_WM_NAME",
210 "_NET_WM_ICON_NAME",
211 "UTF8_STRING",
212 "TARGETS",
213 "INCR",
214 "PIXEL",
215 "FOREGROUND",
216 "BACKGROUND",
217 "_MOTIF_WM_HINTS",
218 "_NET_WM_STATE_MODAL",
219 "_NET_SUPPORTED",
220 "_NET_WM_STATE_MAXIMIZED_HORIZ",
221 "text/plain;charset=utf-8",
222 "_NET_WM_STATE_STAYS_ON_TOP",
223 "_NET_CURRENT_DESKTOP",
224 "_NET_WORKAREA",
225 "_NET_WM_STATE_ABOVE",
226 "XdndEnter",
227 "XdndPosition",
228 "XdndStatus",
229 "XdndTypeList",
230 "XdndActionCopy",
231 "XdndDrop",
232 "XdndLeave",
233 "XdndFinished",
234 "XdndSelection",
235 "XdndProxy",
236 "XdndAware",
237 "XdndActionMove",
238 "XdndActionLink",
239 "XdndActionAsk",
240 "XdndActionPrivate",
241 "XdndActionList",
242 "XdndActionDescription",
243 "text/plain"
244 };
245 char hostname_buf[256], *hostname = hostname_buf, *env;
246
247 guts. click_time_frame = 200;
248 guts. double_click_time_frame = 200;
249 guts. visible_timeout = 500;
250 guts. invisible_timeout = 500;
251 guts. insert = true;
252 guts. last_time = CurrentTime;
253
254 guts. ri_head = guts. ri_tail = 0;
255 DISP = XOpenDisplay( do_display);
256
257 if (!DISP) {
258 char * disp = getenv("DISPLAY");
259 snprintf( error_buf, 256, "Error: Can't open display '%s'",
260 do_display ? do_display : (disp ? disp : ""));
261 free( do_display);
262 do_display = NULL;
263 return false;
264 }
265 free( do_display);
266 do_display = NULL;
267 XSetErrorHandler( x_error_handler);
268 guts.main_error_handler = x_error_handler;
269 (void)x_io_error_handler;
270 XCHECKPOINT;
271 guts.connection = ConnectionNumber( DISP);
272
273 {
274 struct sockaddr name;
275 socklen_t l = sizeof( name);
276 guts. local_connection = getsockname( guts.connection, &name, &l) >= 0 && l == 0;
277 }
278
279 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
280 if ( XShapeQueryExtension( DISP, &guts.shape_event, &guts.shape_error)) {
281 guts. shape_extension = true;
282 } else {
283 guts. shape_extension = false;
284 }
285 #else
286 guts. shape_extension = false;
287 #endif
288 #ifdef USE_MITSHM
289 if ( !do_no_shmem && XShmQueryExtension( DISP)) {
290 guts. shared_image_extension = true;
291 guts. shared_image_completion_event = XShmGetEventBase( DISP) + ShmCompletion;
292 } else {
293 guts. shared_image_extension = false;
294 guts. shared_image_completion_event = -1;
295 }
296 #else
297 guts. shared_image_extension = false;
298 guts. shared_image_completion_event = -1;
299 #endif
300 guts. randr_extension = false;
301 #ifdef HAVE_X11_EXTENSIONS_XRANDR_H
302 {
303 int dummy, major, minor;
304 if ( XRRQueryExtension( DISP, &dummy, &dummy)) {
305 if ( XRRQueryVersion( DISP, &major, &minor)) {
306 if ( major > 1 || (major == 1 && minor > 2 )) {
307 /* XRRGetScreenResourcesCurrent appeared in 1.3 */
308 guts. randr_extension = true;
309 }
310 }
311 }
312 }
313 #endif
314 #ifdef HAVE_X11_EXTENSIONS_XCOMPOSITE_H
315 {
316 int dummy;
317 if (XQueryExtension(DISP, COMPOSITE_NAME, &guts.composite_opcode, &dummy, &dummy))
318 guts. composite_extension = true;
319 }
320 #endif
321 XrmInitialize();
322 guts.db = get_database();
323 XrmStringToQuarkList( common_quarks, common_quarks_list);
324 guts.qString = *ql++;
325 guts.qBlinkinvisibletime = *ql++;
326 guts.qblinkinvisibletime = *ql++;
327 guts.qBlinkvisibletime = *ql++;
328 guts.qblinkvisibletime = *ql++;
329 guts.qClicktimeframe = *ql++;
330 guts.qclicktimeframe = *ql++;
331 guts.qDoubleclicktimeframe = *ql++;
332 guts.qdoubleclicktimeframe = *ql++;
333 guts.qWheeldown = *ql++;
334 guts.qwheeldown = *ql++;
335 guts.qWheelup = *ql++;
336 guts.qwheelup = *ql++;
337 guts.qSubmenudelay = *ql++;
338 guts.qsubmenudelay = *ql++;
339 guts.qScrollfirst = *ql++;
340 guts.qscrollfirst = *ql++;
341 guts.qScrollnext = *ql++;
342 guts.qscrollnext = *ql++;
343
344 guts. mouse_buttons = XGetPointerMapping( DISP, guts. buttons_map, 256);
345 XCHECKPOINT;
346
347 guts. limits. request_length = XMaxRequestSize( DISP);
348 guts. limits. XDrawLines = guts. limits. request_length - 3;
349 guts. limits. XFillPolygon = guts. limits. request_length - 4;
350 guts. limits. XDrawSegments = (guts. limits. request_length - 3) / 2;
351 guts. limits. XDrawRectangles = (guts. limits. request_length - 3) / 2;
352 guts. limits. XFillRectangles = (guts. limits. request_length - 3) / 2;
353 guts. limits. XFillArcs =
354 guts. limits. XDrawArcs = (guts. limits. request_length - 3) / 3;
355 XCHECKPOINT;
356 SCREEN = DefaultScreen( DISP);
357
358 /* XXX - return code? */
359 guts. root = RootWindow( DISP, SCREEN);
360 guts. displaySize. x = DisplayWidth( DISP, SCREEN);
361 guts. displaySize. y = DisplayHeight( DISP, SCREEN);
362 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
363 guts. cursor_width = guts. cursor_height = XcursorGetDefaultSize(DISP);
364 #else
365 XQueryBestCursor( DISP, guts. root,
366 guts. displaySize. x, /* :-) */
367 guts. displaySize. y,
368 &guts. cursor_width,
369 &guts. cursor_height);
370 #endif
371 XCHECKPOINT;
372
373 TAILQ_INIT( &guts.paintq);
374 TAILQ_INIT( &guts.peventq);
375 TAILQ_INIT( &guts.bitmap_gc_pool);
376 TAILQ_INIT( &guts.screen_gc_pool);
377 TAILQ_INIT( &guts.argb_gc_pool);
378
379 guts. currentFocusTime = CurrentTime;
380 guts. windows = hash_create();
381 guts. menu_windows = hash_create();
382 guts. ximages = hash_create();
383 gcv. graphics_exposures = false;
384 guts. menugc = XCreateGC( DISP, guts. root, GCGraphicsExposures, &gcv);
385 guts. resolution. x = ( DisplayWidthMM( DISP, SCREEN) > 0) ?
386 25.4 * guts. displaySize. x / DisplayWidthMM( DISP, SCREEN) + .5:
387 96;
388 guts. resolution. y = ( DisplayHeightMM( DISP, SCREEN) > 0) ?
389 25.4 * DisplayHeight( DISP, SCREEN) / DisplayHeightMM( DISP, SCREEN) + .5:
390 96;
391 guts. depth = DefaultDepth( DISP, SCREEN);
392 guts. idepth = get_idepth();
393 if ( guts.depth == 1) guts. qdepth = 1; else
394 if ( guts.depth <= 4) guts. qdepth = 4; else
395 if ( guts.depth <= 8) guts. qdepth = 8; else
396 guts. qdepth = 24;
397 guts. byte_order = ImageByteOrder( DISP);
398 guts. bit_order = BitmapBitOrder( DISP);
399 if ( BYTEORDER == LSB32 || BYTEORDER == LSB64)
400 guts. machine_byte_order = LSBFirst;
401 else if ( BYTEORDER == MSB32 || BYTEORDER == MSB64)
402 guts. machine_byte_order = MSBFirst;
403 else {
404 sprintf( error_buf, "UAA_001: weird machine byte order: %08x", BYTEORDER);
405 return false;
406 }
407
408 XInternAtoms( DISP, atom_names, AI_count, 0, guts. atoms);
409
410 guts. null_pointer = NULL_HANDLE;
411 guts. pointer_invisible_count = 0;
412 guts. files = plist_create( 16, 16);
413 prima_rebuild_watchers();
414 guts. wm_event_timeout = 100;
415 guts. menu_timeout = 200;
416 guts. scroll_first = 200;
417 guts. scroll_next = 50;
418 apc_timer_create( CURSOR_TIMER);
419 apc_timer_set_timeout(CURSOR_TIMER, 2);
420 apc_timer_create( MENU_TIMER);
421 apc_timer_set_timeout( MENU_TIMER, guts. menu_timeout);
422 apc_timer_create( MENU_UNFOCUS_TIMER);
423 apc_timer_set_timeout( MENU_UNFOCUS_TIMER, 50);
424 if ( !prima_init_clipboard_subsystem (error_buf)) return false;
425 if ( !prima_init_color_subsystem (error_buf)) return false;
426 if ( !do_no_xrender)
427 if ( !prima_init_xrender_subsystem(error_buf)) return false;
428 if ( !prima_init_font_subsystem (error_buf)) return false;
429 #ifdef WITH_GTK
430 guts. use_gtk = do_no_gtk ? false : ( prima_gtk_init() != NULL );
431 #endif
432 #ifdef WITH_COCOA
433 if ( prima_cocoa_is_x11_local())
434 guts. use_quartz = !do_no_quartz;
435 #endif
436 bzero( &guts. cursor_gcv, sizeof( guts. cursor_gcv));
437 guts. cursor_gcv. cap_style = CapButt;
438 guts. cursor_gcv. function = GXcopy;
439
440 gethostname( hostname, 256);
441 hostname[255] = '\0';
442 XStringListToTextProperty((char **)&hostname, 1, &guts. hostname);
443
444 guts. net_wm_maximization = prima_wm_net_state_read_maximization( guts. root, NET_SUPPORTED);
445
446 env = getenv("XDG_SESSION_TYPE");
447 if (( env != NULL) && (strcmp(env, "wayland") == 0)) {
448 guts. is_xwayland = true;
449 Mdebug("XWayland detected\n");
450 }
451
452 if ( do_sync) XSynchronize( DISP, true);
453 return true;
454 }
455
456 Bool
window_subsystem_init(char * error_buf)457 window_subsystem_init( char * error_buf)
458 {
459 bzero( &guts, sizeof( guts));
460 guts. debug = do_debug;
461 guts. icccm_only = do_icccm_only;
462 Mdebug("init x11:%d, debug:%x, sync:%d, display:%s\n", do_x11, guts.debug,
463 do_sync, do_display ? do_display : "(default)");
464 if ( do_x11) {
465 Bool ret = init_x11( error_buf );
466 if ( !ret && DISP) {
467 XCloseDisplay(DISP);
468 DISP = NULL;
469 }
470 return ret;
471 }
472 return true;
473 }
474
475 int
prima_debug(const char * format,...)476 prima_debug( const char *format, ...)
477 {
478 int rc = 0;
479 va_list args;
480 va_start( args, format);
481 rc = vfprintf( stderr, format, args);
482 va_end( args);
483 return rc;
484 }
485
486 Bool
window_subsystem_get_options(int * argc,char *** argv)487 window_subsystem_get_options( int * argc, char *** argv)
488 {
489 static char * x11_argv[] = {
490 "no-x11", "runs Prima without X11 display initialized",
491 "display", "selects X11 DISPLAY (--display=:0.0)",
492 "visual", "X visual id (--visual=0x21, run `xdpyinfo` for list of supported visuals)",
493 "sync", "synchronize X connection",
494 "icccm", "do not use NET_WM (kde/gnome) and MOTIF extensions, ICCCM only",
495 "debug", "turns on debugging on subsystems, selected by characters (--debug=FC). "\
496 "Recognized characters are: "\
497 " 0(none),"\
498 " C(clipboard),"\
499 " E(events),"\
500 " F(fonts),"\
501 " M(miscellaneous),"\
502 " P(palettes and colors),"\
503 " X(XRDB),"\
504 " A(all together)",
505 #ifdef USE_MITSHM
506 "no-shmem", "do not use shared memory for images",
507 #endif
508 "no-core-fonts", "do not use core fonts",
509 #ifdef USE_XFT
510 "no-xft", "do not use XFT",
511 "no-aa", "do not anti-alias XFT fonts",
512 "font-priority", "match unknown fonts against: 'xft' (default) or 'core'",
513 #endif
514 #ifdef WITH_GTK
515 "no-gtk", "do not use GTK",
516 #endif
517 #ifdef WITH_HARFBUZZ
518 "no-harfbuzz", "do not use harfbuzz",
519 #endif
520 #ifdef WITH_COCOA
521 "no-quartz", "do not use Quartz",
522 #endif
523 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
524 "no-xrender", "do not use XRender",
525 #endif
526 "font",
527 #ifdef USE_XFT
528 "default prima font in XLFD (-helv-misc-*-*-) or XFT(Helv-12) format",
529 #else
530 "default prima font in XLFD (-helv-misc-*-*-) format",
531 #endif
532 "menu-font", "default menu font",
533 "msg-font", "default message box font",
534 "widget-font", "default widget font",
535 "caption-font", "MDI caption font",
536 "noscaled", "do not use scaled instances of fonts",
537 "fg", "default foreground color",
538 "bg", "default background color",
539 "hilite-fg", "default highlight foreground color",
540 "hilite-bg", "default highlight background color",
541 "disabled-fg", "default disabled foreground color",
542 "disabled-bg", "default disabled background color",
543 "light", "default light-3d color",
544 "dark", "default dark-3d color"
545 };
546 *argv = x11_argv;
547 *argc = sizeof( x11_argv) / sizeof( char*);
548 return true;
549 }
550
551 Bool
window_subsystem_set_option(char * option,char * value)552 window_subsystem_set_option( char * option, char * value)
553 {
554 Mdebug("%s=%s\n", option, value);
555 if ( strcmp( option, "no-x11") == 0) {
556 if ( value) warn("`--no-x11' option has no parameters");
557 do_x11 = false;
558 return true;
559 } else if ( strcmp( option, "yes-x11") == 0) {
560 do_x11 = true;
561 return true;
562 } else if ( strcmp( option, "display") == 0) {
563 free( do_display);
564 do_display = duplicate_string( value);
565 setenv("DISPLAY", value, 1);
566 return true;
567 } else if ( strcmp( option, "icccm") == 0) {
568 if ( value) warn("`--icccm' option has no parameters");
569 do_icccm_only = true;
570 return true;
571 } else if ( strcmp( option, "no-shmem") == 0) {
572 if ( value) warn("`--no-shmem' option has no parameters");
573 do_no_shmem = true;
574 return true;
575 } else if ( strcmp( option, "no-gtk") == 0) {
576 if ( value) warn("`--no-gtk' option has no parameters");
577 do_no_gtk = true;
578 return true;
579 } else if ( strcmp( option, "no-quartz") == 0) {
580 if ( value) warn("`--no-quartz' option has no parameters");
581 do_no_quartz = true;
582 return true;
583 } else if ( strcmp( option, "no-xrender") == 0) {
584 if ( value) warn("`--no-xrender' option has no parameters");
585 do_no_xrender = true;
586 return true;
587 } else if ( strcmp( option, "debug") == 0) {
588 if ( !value) {
589 warn("`--debug' must be given parameters. `--debug=A` assumed\n");
590 guts. debug |= DEBUG_ALL;
591 do_debug = guts. debug;
592 return true;
593 }
594 while ( *value) switch ( tolower(*(value++))) {
595 case '0':
596 guts. debug = 0;
597 break;
598 case 'c':
599 guts. debug |= DEBUG_CLIP;
600 break;
601 case 'e':
602 guts. debug |= DEBUG_EVENT;
603 break;
604 case 'f':
605 guts. debug |= DEBUG_FONTS;
606 break;
607 case 'm':
608 guts. debug |= DEBUG_MISC;
609 break;
610 case 'p':
611 guts. debug |= DEBUG_COLOR;
612 break;
613 case 'x':
614 guts. debug |= DEBUG_XRDB;
615 break;
616 case 'a':
617 guts. debug |= DEBUG_ALL;
618 break;
619 }
620 do_debug = guts. debug;
621 } else if ( prima_font_subsystem_set_option( option, value)) {
622 return true;
623 } else if ( prima_color_subsystem_set_option( option, value)) {
624 return true;
625 }
626 return false;
627 }
628
629 void
window_subsystem_cleanup(void)630 window_subsystem_cleanup( void)
631 {
632 if ( !DISP) return;
633 /*XXX*/
634 prima_end_menu();
635 #ifdef WITH_GTK
636 if ( guts. use_gtk) prima_gtk_done();
637 #endif
638 }
639
640 static void
free_gc_pool(struct gc_head * head)641 free_gc_pool( struct gc_head *head)
642 {
643 GCList *n1, *n2;
644
645 n1 = TAILQ_FIRST(head);
646 while (n1 != NULL) {
647 n2 = TAILQ_NEXT(n1, gc_link);
648 XFreeGC( DISP, n1-> gc);
649 /* XXX */ free(n1);
650 n1 = n2;
651 }
652 TAILQ_INIT(head);
653 }
654
655 void
window_subsystem_done(void)656 window_subsystem_done( void)
657 {
658 int i;
659 if ( !DISP) return;
660
661 for ( i = 0; i < sizeof(guts.xdnd_pointers) / sizeof(CustomPointer); i++) {
662 CustomPointer *cp = guts.xdnd_pointers + i;
663 if ( cp-> cursor )
664 XFreeCursor( DISP, cp->cursor);
665 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
666 if ( cp-> xcursor != NULL)
667 XcursorImageDestroy(cp-> xcursor);
668 #endif
669 }
670
671 if ( guts. hostname. value) {
672 XFree( guts. hostname. value);
673 guts. hostname. value = NULL;
674 }
675
676 prima_end_menu();
677
678 free_gc_pool(&guts.bitmap_gc_pool);
679 free_gc_pool(&guts.screen_gc_pool);
680 free_gc_pool(&guts.argb_gc_pool);
681 prima_done_color_subsystem();
682 prima_done_xrender_subsystem();
683 free( guts. clipboard_formats);
684
685 XFreeGC( DISP, guts. menugc);
686 prima_gc_ximages(); /* verrry dangerous, very quiet please */
687 if ( guts.pointer_font) {
688 XFreeFont( DISP, guts.pointer_font);
689 guts.pointer_font = NULL;
690 }
691 XCloseDisplay( DISP);
692 DISP = NULL;
693
694 plist_destroy( guts. files);
695 guts. files = NULL;
696
697 XrmDestroyDatabase( guts.db);
698 if (guts.ximages) hash_destroy( guts.ximages, false);
699 if (guts.menu_windows) hash_destroy( guts.menu_windows, false);
700 if (guts.windows) hash_destroy( guts.windows, false);
701 if (guts.clipboards) hash_destroy( guts.clipboards, false);
702 if (guts.clipboard_xfers) hash_destroy( guts.clipboard_xfers, false);
703 prima_cleanup_font_subsystem();
704 bzero(&guts, sizeof(guts));
705 }
706
707 static int
can_access_root_screen(void)708 can_access_root_screen(void)
709 {
710 static int result = -1;
711 XImage * im;
712 XErrorEvent xr;
713
714 if ( result >= 0 ) return result;
715 result = 0;
716
717 XFlush(DISP);
718 prima_save_xerror_event(&xr);
719 im = XGetImage( DISP, guts.root, 0, 0, 1, 1, AllPlanes, ZPixmap); /* XWayland fails here */
720 prima_restore_xerror_event(NULL);
721 if (im == NULL) goto EXIT;
722
723 XDestroyImage( im);
724
725 #ifdef WITH_GTK_NONX11
726 /* detect XQuartz */
727 {
728 char * display_str = getenv("DISPLAY");
729 if ( display_str ) {
730 struct stat s;
731 if ((stat( display_str, &s) >= 0) && S_ISSOCK(s.st_mode)) /* is a socket */
732 goto EXIT;
733 }
734 }
735 #endif
736
737 result = 1;
738 EXIT:
739 return result;
740 }
741
742 Bool
apc_application_begin_paint(Handle self)743 apc_application_begin_paint( Handle self)
744 {
745 DEFXX;
746 if ( guts. appLock > 0) return false;
747 if ( !can_access_root_screen()) return false;
748 prima_prepare_drawable_for_painting( self, false);
749 XX-> flags. force_flush = 1;
750 return true;
751 }
752
753 Bool
apc_application_begin_paint_info(Handle self)754 apc_application_begin_paint_info( Handle self)
755 {
756 prima_prepare_drawable_for_painting( self, false);
757 return true;
758 }
759
760 Bool
apc_application_create(Handle self)761 apc_application_create( Handle self)
762 {
763 XSetWindowAttributes attrs;
764 DEFXX;
765 if ( !DISP) {
766 Mdebug("apc_application_create: failed, x11 layer is not up\n");
767 return false;
768 }
769
770 XX-> type.application = true;
771 XX-> type.widget = true;
772 XX-> type.drawable = true;
773
774 attrs. event_mask = StructureNotifyMask | PropertyChangeMask;
775 XX-> client = X_WINDOW = XCreateWindow( DISP, guts. root,
776 0, 0, 1, 1, 0, CopyFromParent,
777 InputOutput, CopyFromParent,
778 CWEventMask, &attrs);
779 XCHECKPOINT;
780 if (!X_WINDOW) return false;
781 hash_store( guts.windows, &X_WINDOW, sizeof(X_WINDOW), (void*)self);
782
783 XX-> pointer_id = crArrow;
784 XX-> gdrawable = XX-> udrawable = guts. root;
785 XX-> parent = None;
786 XX-> origin. x = 0;
787 XX-> origin. y = 0;
788 XX-> ackSize = XX-> size = apc_application_get_size( self);
789 XX-> owner = NULL_HANDLE;
790 XX-> visual = &guts. visual;
791
792 XX-> flags. clip_owner = 1;
793 XX-> flags. sync_paint = 0;
794
795 apc_component_fullname_changed_notify( self);
796 guts. mouse_wheel_down = unix_rm_get_int( self, guts.qWheeldown, guts.qwheeldown, 5);
797 guts. mouse_wheel_up = unix_rm_get_int( self, guts.qWheelup, guts.qwheelup, 4);
798 guts. click_time_frame = unix_rm_get_int( self, guts.qClicktimeframe, guts.qclicktimeframe, guts. click_time_frame);
799 guts. double_click_time_frame = unix_rm_get_int( self, guts.qDoubleclicktimeframe, guts.qdoubleclicktimeframe, guts. double_click_time_frame);
800 guts. visible_timeout = unix_rm_get_int( self, guts.qBlinkvisibletime, guts.qblinkvisibletime, guts. visible_timeout);
801 guts. invisible_timeout = unix_rm_get_int( self, guts.qBlinkinvisibletime, guts.qblinkinvisibletime, guts. invisible_timeout);
802 guts. menu_timeout = unix_rm_get_int( self, guts.qSubmenudelay, guts.qsubmenudelay, guts. menu_timeout);
803 guts. scroll_first = unix_rm_get_int( self, guts.qScrollfirst, guts.qscrollfirst, guts. scroll_first);
804 guts. scroll_next = unix_rm_get_int( self, guts.qScrollnext, guts.qscrollnext, guts. scroll_next);
805
806 prima_send_create_event( X_WINDOW);
807 return true;
808 }
809
810 Bool
apc_application_close(Handle self)811 apc_application_close( Handle self)
812 {
813 guts. applicationClose = true;
814 return true;
815 }
816
817 Bool
apc_application_destroy(Handle self)818 apc_application_destroy( Handle self)
819 {
820 if ( X_WINDOW) {
821 XDestroyWindow( DISP, X_WINDOW);
822 XCHECKPOINT;
823 hash_delete( guts.windows, (void*)&X_WINDOW, sizeof(X_WINDOW), false);
824 }
825 application = NULL_HANDLE;
826 return true;
827 }
828
829 Bool
apc_application_end_paint(Handle self)830 apc_application_end_paint( Handle self)
831 {
832 DEFXX;
833 XX-> flags. force_flush = 0;
834 prima_cleanup_drawable_after_painting( self);
835 return true;
836 }
837
838 Bool
apc_application_end_paint_info(Handle self)839 apc_application_end_paint_info( Handle self)
840 {
841 prima_cleanup_drawable_after_painting( self);
842 return true;
843 }
844
845 int
apc_application_get_gui_info(char * description,int len1,char * language,int len2)846 apc_application_get_gui_info( char * description, int len1, char * language, int len2)
847 {
848 int ret = guiXLib;
849 if ( description)
850 strncpy( description, "X Window System", len1);
851
852 #ifdef WITH_GTK
853 if ( guts. use_gtk ) {
854 if ( description) {
855 strncat( description, " + GTK", len1);
856 #ifdef WITH_GTK_NONX11
857 strncat( description, " with native support", len1);
858 #endif
859 #ifdef WITH_COCOA
860 if ( guts. use_quartz)
861 strncat( description, " + Cocoa", len1);
862 #endif
863 }
864 ret = guiGTK;
865 }
866 #endif
867
868 if ( description)
869 description[len1-1] = 0;
870
871 if ( language ) {
872 char * lang = getenv("LANG");
873 if ( lang ) {
874 while (len2 > 1 && (*lang == '-' || islower((int)*lang))) {
875 *(language++) = *(lang++);
876 }
877 *language = 0;
878 } else
879 *language = 0;
880 }
881
882 return ret;
883 }
884
885 Handle
apc_application_get_widget_from_point(Handle self,Point p)886 apc_application_get_widget_from_point( Handle self, Point p)
887 {
888 XWindow from, to, child;
889
890 from = to = guts. root;
891 p. y = DisplayHeight( DISP, SCREEN) - p. y - 1;
892 while (XTranslateCoordinates(DISP, from, to, p.x, p.y, &p.x, &p.y, &child)) {
893 if (child) {
894 from = to;
895 to = child;
896 } else {
897 Handle h;
898 if ( to == from) to = X_WINDOW;
899 h = (Handle)hash_fetch( guts.windows, (void*)&to, sizeof(to));
900 return ( h == application) ? NULL_HANDLE : h;
901 }
902 }
903 return NULL_HANDLE;
904 }
905
906 Handle
apc_application_get_handle(Handle self,ApiHandle apiHandle)907 apc_application_get_handle( Handle self, ApiHandle apiHandle)
908 {
909 return prima_xw2h(( XWindow) apiHandle);
910 }
911
912 static Bool
wm_net_get_current_workarea(Rect * r)913 wm_net_get_current_workarea( Rect * r)
914 {
915 Bool ret = false;
916 unsigned long n;
917 unsigned long *desktop = NULL, *workarea = NULL, *w;
918
919 if ( guts. icccm_only) return false;
920
921 desktop = ( unsigned long *) prima_get_window_property( guts. root,
922 NET_CURRENT_DESKTOP, XA_CARDINAL,
923 NULL, NULL,
924 &n);
925 if ( desktop == NULL || n < 1) goto EXIT;
926 Mdebug("wm: current desktop = %d\n", *desktop);
927
928 workarea = ( unsigned long *) prima_get_window_property( guts. root,
929 NET_WORKAREA, XA_CARDINAL,
930 NULL, NULL,
931 &n);
932 if ( desktop == NULL || n < 1 || n <= *desktop ) goto EXIT;
933
934 w = workarea + *desktop * 4; /* XYWH quartets */
935 r-> left = w[0];
936 r-> top = w[1];
937 r-> right = w[2];
938 r-> bottom = w[3];
939 ret = true;
940 Mdebug("wm: current workarea = %d:%d:%d:%d\n", w[0], w[1], w[2], w[3]);
941
942 EXIT:
943 free( workarea);
944 free( desktop);
945 return ret;
946 }
947
948 Rect
apc_application_get_indents(Handle self)949 apc_application_get_indents( Handle self)
950 {
951 Point sz;
952 Rect r;
953
954 bzero( &r, sizeof( r));
955 if ( do_icccm_only) return r;
956
957 sz = apc_application_get_size( self);
958 if ( wm_net_get_current_workarea( &r)) {
959 r. right = sz. x - r.right - r. left;
960 r. bottom = sz. y - r. bottom - r. top;
961 if ( r. left < 0) r. left = 0;
962 if ( r. top < 0) r. top = 0;
963 if ( r. right < 0) r. right = 0;
964 if ( r. bottom < 0) r. bottom = 0;
965 }
966
967 return r;
968 }
969
970 int
apc_application_get_os_info(char * system,int slen,char * release,int rlen,char * vendor,int vlen,char * arch,int alen)971 apc_application_get_os_info( char *system, int slen,
972 char *release, int rlen,
973 char *vendor, int vlen,
974 char *arch, int alen)
975 {
976 static struct utsname name;
977 static Bool fetched = false;
978
979 if (!fetched) {
980 if ( uname(&name)!=0) {
981 strncpy( name. sysname, "Some UNIX", sizeof(name.sysname));
982 name. sysname[ sizeof(name.sysname)-1] = 0;
983 strncpy( name. release, "Unknown version of UNIX", sizeof(name.release));
984 name. release[ sizeof(name.release)-1] = 0;
985 strncpy( name. machine, "Unknown architecture", sizeof(name.machine));
986 name. machine[ sizeof(name.machine)-1] = 0;
987 }
988 fetched = true;
989 }
990
991 if (system) {
992 strncpy( system, name. sysname, slen);
993 system[ slen-1] = 0;
994 }
995 if (release) {
996 strncpy( release, name. release, rlen);
997 release[ rlen-1] = 0;
998 }
999 if (vendor) {
1000 strncpy( vendor, "Unknown vendor", vlen);
1001 vendor[ vlen-1] = 0;
1002 }
1003 if (arch) {
1004 strncpy( arch, name. machine, alen);
1005 arch[ alen-1] = 0;
1006 }
1007
1008 return apcUnix;
1009 }
1010
1011 Point
apc_application_get_size(Handle self)1012 apc_application_get_size( Handle self)
1013 {
1014 return guts. displaySize;
1015 }
1016
1017 Box *
apc_application_get_monitor_rects(Handle self,int * nrects)1018 apc_application_get_monitor_rects( Handle self, int * nrects)
1019 {
1020 #if defined(HAVE_X11_EXTENSIONS_XRANDR_H) && (RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR > 3))
1021 XRRScreenResources * sr;
1022 Box * ret = NULL;
1023
1024 if ( !guts. randr_extension) {
1025 *nrects = 0;
1026 return NULL;
1027 }
1028
1029 XCHECKPOINT;
1030 sr = XRRGetScreenResourcesCurrent(DISP,guts.root);
1031 if ( sr ) {
1032 int i;
1033 ret = malloc(sizeof(Box) * sr->ncrtc);
1034 *nrects = sr->ncrtc;
1035 for ( i = 0; i < sr->ncrtc; i++) {
1036 XRRCrtcInfo * ci = XRRGetCrtcInfo (DISP, sr, sr->crtcs[i]);
1037 ret[i].x = ci->x;
1038 ret[i].y = guts.displaySize.y - ci->height - ci->y;
1039 ret[i].width = ci->width;
1040 ret[i].height = ci->height;
1041 XRRFreeCrtcInfo(ci);
1042 }
1043 XRRFreeScreenResources(sr);
1044 XCHECKPOINT;
1045 } else {
1046 *nrects = 0;
1047 }
1048 return ret;
1049 #else
1050 *nrects = 0;
1051 return NULL;
1052 #endif
1053 }
1054
1055 Bool
apc_application_go(Handle self)1056 apc_application_go( Handle self)
1057 {
1058 if (!application) return false;
1059
1060 XNoOp( DISP);
1061 XFlush( DISP);
1062 guts. application_stop_signal = false;
1063 while ( !guts. application_stop_signal && prima_one_loop_round( WAIT_ALWAYS, true))
1064 ;
1065 guts. application_stop_signal = false;
1066 return true;
1067 }
1068
1069 Bool
apc_application_lock(Handle self)1070 apc_application_lock( Handle self)
1071 {
1072 guts. appLock++;
1073 return true;
1074 }
1075
1076 Bool
apc_application_unlock(Handle self)1077 apc_application_unlock( Handle self)
1078 {
1079 if ( guts. appLock > 0) guts. appLock--;
1080 return true;
1081 }
1082
1083 Bool
apc_application_stop(Handle self)1084 apc_application_stop( Handle self)
1085 {
1086 if ( application == NULL_HANDLE ) return false;
1087 guts. application_stop_signal = true;
1088 return true;
1089 }
1090
1091 Bool
apc_application_sync(void)1092 apc_application_sync(void)
1093 {
1094 XSync( DISP, false);
1095 return true;
1096 }
1097
1098 Bool
apc_application_yield(Bool wait_for_event)1099 apc_application_yield( Bool wait_for_event)
1100 {
1101 if (!application) return false;
1102 guts. application_stop_signal = false;
1103 prima_one_loop_round(wait_for_event ? WAIT_IF_NONE : WAIT_NEVER, true);
1104 guts. application_stop_signal = false;
1105 XSync( DISP, false);
1106 return application != NULL_HANDLE && !guts. applicationClose;
1107 }
1108