Make wmaker XRandR aware
[wmaker-crm.git] / src / screen.c
1 /* screen.c - screen management
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
21 */
22
23 #include "wconfig.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/Xatom.h>
32 #ifdef SHAPE
33 #include <X11/extensions/shape.h>
34 #endif
35 #ifdef KEEP_XKB_LOCK_STATUS
36 #include <X11/XKBlib.h>
37 #endif /* KEEP_XKB_LOCK_STATUS */
38 #ifdef HAVE_XRANDR
39 #include <X11/extensions/Xrandr.h>
40 #endif
41
42 #include <wraster.h>
43 #include "WindowMaker.h"
44 #include "def_pixmaps.h"
45 #include "screen.h"
46 #include "texture.h"
47 #include "pixmap.h"
48 #include "menu.h"
49 #include "funcs.h"
50 #include "actions.h"
51 #include "properties.h"
52 #include "dock.h"
53 #include "resources.h"
54 #include "workspace.h"
55 #include "session.h"
56 #include "balloon.h"
57 #include "geomview.h"
58 #include "wmspec.h"
59
60 #include "xinerama.h"
61
62 #include <WINGs/WUtil.h>
63
64 #include "defaults.h"
65
66 #define EVENT_MASK (LeaveWindowMask|EnterWindowMask|PropertyChangeMask\
67 |SubstructureNotifyMask|PointerMotionMask \
68 |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
69 |KeyPressMask|KeyReleaseMask)
70
71 /**** Global variables ****/
72 extern Cursor wCursor[WCUR_LAST];
73 extern WPreferences wPreferences;
74 extern Atom _XA_WINDOWMAKER_STATE;
75 extern Atom _XA_WINDOWMAKER_NOTICEBOARD;
76
77 extern int wScreenCount;
78
79 #ifdef KEEP_XKB_LOCK_STATUS
80 extern int wXkbSupported;
81 #endif
82 #ifdef HAVE_XRANDR
83 extern int has_randr;
84 #endif
85
86 extern WDDomain *WDWindowMaker;
87
88 /**** Local ****/
89 #define STIPPLE_WIDTH 2
90 #define STIPPLE_HEIGHT 2
91 static char STIPPLE_DATA[] = { 0x02, 0x01 };
92
93 static int CantManageScreen = 0;
94
95 static WMPropList *dApplications = NULL;
96 static WMPropList *dWorkspace;
97 static WMPropList *dDock;
98 static WMPropList *dClip;
99
100 static void make_keys(void)
101 {
102 if (dApplications != NULL)
103 return;
104
105 dApplications = WMCreatePLString("Applications");
106 dWorkspace = WMCreatePLString("Workspace");
107 dDock = WMCreatePLString("Dock");
108 dClip = WMCreatePLString("Clip");
109 }
110
111 /*
112 *----------------------------------------------------------------------
113 * alreadyRunningError--
114 * X error handler used to catch errors when trying to do
115 * XSelectInput() on the root window. These errors probably mean that
116 * there already is some other window manager running.
117 *
118 * Returns:
119 * Nothing, unless something really evil happens...
120 *
121 * Side effects:
122 * CantManageScreen is set to 1;
123 *----------------------------------------------------------------------
124 */
125 static int alreadyRunningError(Display * dpy, XErrorEvent * error)
126 {
127 CantManageScreen = 1;
128 return -1;
129 }
130
131 /*
132 *----------------------------------------------------------------------
133 * allocButtonPixmaps--
134 * Allocate pixmaps used on window operation buttons (those in the
135 * titlebar). The pixmaps are linked to the program. If XPM is supported
136 * XPM pixmaps are used otherwise, equivalent bitmaps are used.
137 *
138 * Returns:
139 * Nothing
140 *
141 * Side effects:
142 * Allocates shared pixmaps for the screen. These pixmaps should
143 * not be freed by anybody.
144 *----------------------------------------------------------------------
145 */
146 static void allocButtonPixmaps(WScreen * scr)
147 {
148 WPixmap *pix;
149
150 /* create predefined pixmaps */
151 pix = wPixmapCreateFromXPMData(scr, PRED_CLOSE_XPM);
152 if (pix)
153 pix->shared = 1;
154 scr->b_pixmaps[WBUT_CLOSE] = pix;
155
156 pix = wPixmapCreateFromXPMData(scr, PRED_BROKEN_CLOSE_XPM);
157 if (pix)
158 pix->shared = 1;
159 scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;
160
161 pix = wPixmapCreateFromXPMData(scr, PRED_ICONIFY_XPM);
162 if (pix)
163 pix->shared = 1;
164 scr->b_pixmaps[WBUT_ICONIFY] = pix;
165 #ifdef XKB_BUTTON_HINT
166 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP1_XPM);
167 if (pix)
168 pix->shared = 1;
169 scr->b_pixmaps[WBUT_XKBGROUP1] = pix;
170 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP2_XPM);
171 if (pix)
172 pix->shared = 1;
173 scr->b_pixmaps[WBUT_XKBGROUP2] = pix;
174 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP3_XPM);
175 if (pix)
176 pix->shared = 1;
177 scr->b_pixmaps[WBUT_XKBGROUP3] = pix;
178 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP4_XPM);
179 if (pix)
180 pix->shared = 1;
181 scr->b_pixmaps[WBUT_XKBGROUP4] = pix;
182 #endif
183
184 pix = wPixmapCreateFromXPMData(scr, PRED_KILL_XPM);
185 if (pix)
186 pix->shared = 1;
187 scr->b_pixmaps[WBUT_KILL] = pix;
188 }
189
190 static void draw_dot(WScreen * scr, Drawable d, int x, int y, GC gc)
191 {
192 XSetForeground(dpy, gc, scr->black_pixel);
193 XDrawLine(dpy, d, gc, x, y, x + 1, y);
194 XDrawPoint(dpy, d, gc, x, y + 1);
195 XSetForeground(dpy, gc, scr->white_pixel);
196 XDrawLine(dpy, d, gc, x + 2, y, x + 2, y + 1);
197 XDrawPoint(dpy, d, gc, x + 1, y + 1);
198 }
199
200 static WPixmap *make3Dots(WScreen * scr)
201 {
202 WPixmap *wpix;
203 GC gc2, gc;
204 XGCValues gcv;
205 Pixmap pix, mask;
206
207 gc = scr->copy_gc;
208 pix = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth);
209 XSetForeground(dpy, gc, scr->black_pixel);
210 XFillRectangle(dpy, pix, gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
211 XSetForeground(dpy, gc, scr->white_pixel);
212 draw_dot(scr, pix, 4, wPreferences.icon_size - 6, gc);
213 draw_dot(scr, pix, 9, wPreferences.icon_size - 6, gc);
214 draw_dot(scr, pix, 14, wPreferences.icon_size - 6, gc);
215
216 mask = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, 1);
217 gcv.foreground = 0;
218 gcv.graphics_exposures = False;
219 gc2 = XCreateGC(dpy, mask, GCForeground | GCGraphicsExposures, &gcv);
220 XFillRectangle(dpy, mask, gc2, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
221 XSetForeground(dpy, gc2, 1);
222 XFillRectangle(dpy, mask, gc2, 4, wPreferences.icon_size - 6, 3, 2);
223 XFillRectangle(dpy, mask, gc2, 9, wPreferences.icon_size - 6, 3, 2);
224 XFillRectangle(dpy, mask, gc2, 14, wPreferences.icon_size - 6, 3, 2);
225
226 XFreeGC(dpy, gc2);
227
228 wpix = wPixmapCreate(scr, pix, mask);
229 wpix->shared = 1;
230
231 return wpix;
232 }
233
234 static void allocGCs(WScreen * scr)
235 {
236 XGCValues gcv;
237 XColor color;
238 int gcm;
239
240 scr->stipple_bitmap = XCreateBitmapFromData(dpy, scr->w_win, STIPPLE_DATA, STIPPLE_WIDTH, STIPPLE_HEIGHT);
241
242 gcv.stipple = scr->stipple_bitmap;
243 gcv.foreground = scr->white_pixel;
244 gcv.fill_style = FillStippled;
245 gcv.graphics_exposures = False;
246 gcm = GCForeground | GCStipple | GCFillStyle | GCGraphicsExposures;
247 scr->stipple_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
248
249 /* selected icon border GCs */
250 gcv.function = GXcopy;
251 gcv.foreground = scr->white_pixel;
252 gcv.background = scr->black_pixel;
253 gcv.line_width = 1;
254 gcv.line_style = LineDoubleDash;
255 gcv.fill_style = FillSolid;
256 gcv.dash_offset = 0;
257 gcv.dashes = 4;
258 gcv.graphics_exposures = False;
259
260 gcm = GCFunction | GCGraphicsExposures;
261 gcm |= GCForeground | GCBackground;
262 gcm |= GCLineWidth | GCLineStyle;
263 gcm |= GCFillStyle;
264 gcm |= GCDashOffset | GCDashList;
265
266 scr->icon_select_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
267
268 scr->menu_title_color[0] = WMRetainColor(scr->white);
269
270 /* don't retain scr->black here because we may alter its alpha */
271 scr->mtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
272 scr->dtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
273
274 /* frame GC */
275 wGetColor(scr, DEF_FRAME_COLOR, &color);
276 gcv.function = GXxor;
277 /* this will raise the probability of the XORed color being different
278 * of the original color in PseudoColor when not all color cells are
279 * initialized */
280 if (DefaultVisual(dpy, scr->screen)->class == PseudoColor)
281 gcv.plane_mask = (1 << (scr->depth - 1)) | 1;
282 else
283 gcv.plane_mask = AllPlanes;
284 gcv.foreground = color.pixel;
285 if (gcv.foreground == 0)
286 gcv.foreground = 1;
287 gcv.line_width = DEF_FRAME_THICKNESS;
288 gcv.subwindow_mode = IncludeInferiors;
289 gcv.graphics_exposures = False;
290 scr->frame_gc = XCreateGC(dpy, scr->root_win, GCForeground | GCGraphicsExposures
291 | GCFunction | GCSubwindowMode | GCLineWidth | GCPlaneMask, &gcv);
292
293 /* line GC */
294 gcv.foreground = color.pixel;
295
296 if (gcv.foreground == 0)
297 /* XOR:ing with a zero is not going to be of much use, so
298 in that case, we somewhat arbitrarily xor with 17 instead. */
299 gcv.foreground = 17;
300
301 gcv.function = GXxor;
302 gcv.subwindow_mode = IncludeInferiors;
303 gcv.line_width = 1;
304 gcv.cap_style = CapRound;
305 gcv.graphics_exposures = False;
306 gcm = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth | GCCapStyle | GCGraphicsExposures;
307 scr->line_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);
308
309 scr->line_pixel = gcv.foreground;
310
311 /* copy GC */
312 gcv.foreground = scr->white_pixel;
313 gcv.background = scr->black_pixel;
314 gcv.graphics_exposures = False;
315 scr->copy_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
316
317 /* misc drawing GC */
318 gcv.graphics_exposures = False;
319 gcm = GCGraphicsExposures;
320 scr->draw_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
321
322 assert(scr->stipple_bitmap != None);
323
324 /* mono GC */
325 scr->mono_gc = XCreateGC(dpy, scr->stipple_bitmap, gcm, &gcv);
326 }
327
328 static void createPixmaps(WScreen * scr)
329 {
330 WPixmap *pix;
331 RImage *image;
332
333 /* load pixmaps */
334 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_RADIO_INDICATOR_XBM_DATA,
335 (char *)MENU_RADIO_INDICATOR_XBM_DATA,
336 MENU_RADIO_INDICATOR_XBM_SIZE,
337 MENU_RADIO_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
338 if (pix != NULL)
339 pix->shared = 1;
340 scr->menu_radio_indicator = pix;
341
342 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_CHECK_INDICATOR_XBM_DATA,
343 (char *)MENU_CHECK_INDICATOR_XBM_DATA,
344 MENU_CHECK_INDICATOR_XBM_SIZE,
345 MENU_CHECK_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
346 if (pix != NULL)
347 pix->shared = 1;
348 scr->menu_check_indicator = pix;
349
350 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_MINI_INDICATOR_XBM_DATA,
351 (char *)MENU_MINI_INDICATOR_XBM_DATA,
352 MENU_MINI_INDICATOR_XBM_SIZE,
353 MENU_MINI_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
354 if (pix != NULL)
355 pix->shared = 1;
356 scr->menu_mini_indicator = pix;
357
358 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_HIDE_INDICATOR_XBM_DATA,
359 (char *)MENU_HIDE_INDICATOR_XBM_DATA,
360 MENU_HIDE_INDICATOR_XBM_SIZE,
361 MENU_HIDE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
362 if (pix != NULL)
363 pix->shared = 1;
364 scr->menu_hide_indicator = pix;
365
366 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_SHADE_INDICATOR_XBM_DATA,
367 (char *)MENU_SHADE_INDICATOR_XBM_DATA,
368 MENU_SHADE_INDICATOR_XBM_SIZE,
369 MENU_SHADE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
370 if (pix != NULL)
371 pix->shared = 1;
372 scr->menu_shade_indicator = pix;
373
374 image = wDefaultGetImage(scr, "Logo", "WMPanel");
375
376 if (!image) {
377 wwarning(_("could not load logo image for panels: %s"), RMessageForError(RErrorCode));
378 } else {
379 WMSetApplicationIconImage(scr->wmscreen, image);
380 RReleaseImage(image);
381 }
382
383 scr->dock_dots = make3Dots(scr);
384
385 /* titlebar button pixmaps */
386 allocButtonPixmaps(scr);
387 }
388
389 /*
390 *----------------------------------------------------------------------
391 * createInternalWindows--
392 * Creates some windows used internally by the program. One to
393 * receive input focus when no other window can get it and another
394 * to display window geometry information during window resize/move.
395 *
396 * Returns:
397 * Nothing
398 *
399 * Side effects:
400 * Windows are created and some colors are allocated for the
401 * window background.
402 *----------------------------------------------------------------------
403 */
404 static void createInternalWindows(WScreen * scr)
405 {
406 int vmask;
407 XSetWindowAttributes attribs;
408
409 /* InputOnly window to get the focus when no other window can get it */
410 vmask = CWEventMask | CWOverrideRedirect;
411 attribs.event_mask = KeyPressMask | FocusChangeMask;
412 attribs.override_redirect = True;
413 scr->no_focus_win = XCreateWindow(dpy, scr->root_win, -10, -10, 4, 4, 0, 0,
414 InputOnly, CopyFromParent, vmask, &attribs);
415 XSelectInput(dpy, scr->no_focus_win, KeyPressMask | KeyReleaseMask);
416 XMapWindow(dpy, scr->no_focus_win);
417
418 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
419
420 /* shadow window for dock buttons */
421 vmask = CWBorderPixel | CWBackPixmap | CWBackPixel | CWCursor | CWSaveUnder | CWOverrideRedirect;
422 attribs.border_pixel = scr->black_pixel;
423 attribs.save_under = True;
424 attribs.override_redirect = True;
425 attribs.background_pixmap = None;
426 attribs.background_pixel = scr->white_pixel;
427 attribs.cursor = wCursor[WCUR_DEFAULT];
428 vmask |= CWColormap;
429 attribs.colormap = scr->w_colormap;
430 scr->dock_shadow =
431 XCreateWindow(dpy, scr->root_win, 0, 0, wPreferences.icon_size,
432 wPreferences.icon_size, 0, scr->w_depth, CopyFromParent, scr->w_visual, vmask, &attribs);
433
434 /* workspace name balloon for clip */
435 vmask = CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWColormap | CWBorderPixel;
436 attribs.save_under = True;
437 attribs.override_redirect = True;
438 attribs.colormap = scr->w_colormap;
439 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
440 attribs.border_pixel = 0; /* do not care */
441 scr->clip_balloon =
442 XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
443 CopyFromParent, scr->w_visual, vmask, &attribs);
444
445 /* workspace name */
446 vmask = CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWColormap | CWBorderPixel;
447 attribs.save_under = True;
448 attribs.override_redirect = True;
449 attribs.colormap = scr->w_colormap;
450 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
451 attribs.border_pixel = 0; /* do not care */
452 scr->workspace_name =
453 XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
454 CopyFromParent, scr->w_visual, vmask, &attribs);
455
456 /*
457 * If the window is clicked without having ButtonPress selected, the
458 * resulting event will have event.xbutton.window == root.
459 */
460 XSelectInput(dpy, scr->clip_balloon, ButtonPressMask);
461 }
462
463 #if 0
464 static Bool aquireManagerSelection(WScreen * scr)
465 {
466 char buffer[32];
467 XEvent ev;
468 Time timestamp;
469
470 snprintf(buffer, sizeof(buffer), "WM_S%i", scr->screen);
471 scr->managerAtom = XInternAtom(dpy, buffer, False);
472
473 /* for race-conditions... */
474 XGrabServer(dpy);
475
476 /* if there is another manager running, don't try to replace it
477 * (for now, at least) */
478 if (XGetSelectionOwner(dpy, scr->managerAtom) != None) {
479 XUngrabServer(dpy);
480 return False;
481 }
482
483 /* become the manager for this screen */
484
485 scr->managerWindow = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 1, 1, 0, 0, 0);
486
487 XSelectInput(dpy, scr->managerWindow, PropertyChangeMask);
488 /* get a timestamp */
489 XChangeProperty(dpy, scr->managerWindow, scr->managerAtom, XA_INTEGER, 32, PropModeAppend, NULL, 0);
490 while (1) {
491 XWindowEvent(dpy, scr->managerWindow, &ev);
492 if (ev.type == PropertyNotify) {
493 timestamp = ev.xproperty.time;
494 break;
495 }
496 }
497 XSelectInput(dpy, scr->managerWindow, NoEvents);
498 XDeleteProperty(dpy, scr->managerWindow, scr->managerAtom);
499
500 XSetSelectionOwner(dpy, scr->managerAtom, scr->managerWindow, CurrentTime);
501
502 XUngrabServer(dpy);
503
504 /* announce our arrival */
505
506 ev.xclient.type = ClientMessage;
507 ev.xclient.message_type = XInternAtom(dpy, "MANAGER", False);
508 ev.xclient.destination = scr->root_win;
509 ev.xclient.format = 32;
510 ev.xclient.data.l[0] = timestamp;
511 ev.xclient.data.l[1] = scr->managerAtom;
512 ev.xclient.data.l[2] = scr->managerWindow;
513 ev.xclient.data.l[3] = 0;
514 ev.xclient.data.l[4] = 0;
515
516 XSendEvent(dpy, scr->root_win, False, StructureNotify, &ev);
517 XSync(dpy, False);
518
519 return True;
520 }
521 #endif
522
523 /*
524 *----------------------------------------------------------------------
525 * wScreenInit--
526 * Initializes the window manager for the given screen and
527 * allocates a WScreen descriptor for it. Many resources are allocated
528 * for the screen and the root window is setup appropriately.
529 *
530 * Returns:
531 * The WScreen descriptor for the screen.
532 *
533 * Side effects:
534 * Many resources are allocated and the IconSize property is
535 * set on the root window.
536 * The program can be aborted if some fatal error occurs.
537 *
538 * TODO: User specifiable visual.
539 *----------------------------------------------------------------------
540 */
541 WScreen *wScreenInit(int screen_number)
542 {
543 WScreen *scr;
544 XIconSize icon_size[1];
545 RContextAttributes rattr;
546 long event_mask;
547 XErrorHandler oldHandler;
548 int i;
549
550 scr = wmalloc(sizeof(WScreen));
551 memset(scr, 0, sizeof(WScreen));
552
553 scr->stacking_list = WMCreateTreeBag();
554
555 /* initialize globals */
556 scr->screen = screen_number;
557 scr->root_win = RootWindow(dpy, screen_number);
558 scr->depth = DefaultDepth(dpy, screen_number);
559 scr->colormap = DefaultColormap(dpy, screen_number);
560
561 scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
562 scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
563
564 wInitXinerama(scr);
565
566 scr->usableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
567 scr->totalUsableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
568
569 for (i = 0; i < wXineramaHeads(scr); ++i) {
570 WMRect rect = wGetRectForHead(scr, i);
571 scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = rect.pos.x;
572 scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = rect.pos.y;
573 scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
574 scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
575 }
576
577 scr->fakeGroupLeaders = WMCreateArray(16);
578
579 #if 0
580 if (!aquireManagerSelection(scr)) {
581 wfree(scr);
582
583 return NULL;
584 }
585 #endif
586 CantManageScreen = 0;
587 oldHandler = XSetErrorHandler((XErrorHandler) alreadyRunningError);
588
589 event_mask = EVENT_MASK;
590
591 if (wPreferences.disable_root_mouse) {
592 event_mask &= ~(ButtonPressMask | ButtonReleaseMask);
593 }
594
595 XSelectInput(dpy, scr->root_win, event_mask);
596
597 #ifdef KEEP_XKB_LOCK_STATUS
598 /* Only GroupLock doesn't work correctly in my system since right-alt
599 * can change mode while holding it too - ]d
600 */
601 if (wXkbSupported) {
602 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask);
603 }
604 #endif /* KEEP_XKB_LOCK_STATUS */
605
606 #ifdef HAVE_XRANDR
607 if (has_randr)
608 XRRSelectInput(dpy, scr->root_win, RRScreenChangeNotifyMask);
609 #endif
610
611 XSync(dpy, False);
612 XSetErrorHandler(oldHandler);
613
614 if (CantManageScreen) {
615 wfree(scr);
616 return NULL;
617 }
618
619 XDefineCursor(dpy, scr->root_win, wCursor[WCUR_ROOT]);
620
621 /* screen descriptor for raster graphic library */
622 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap;
623 rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering;
624
625 /* if the std colormap stuff works ok, this will be ignored */
626 rattr.colors_per_channel = wPreferences.cmap_size;
627 if (rattr.colors_per_channel < 2)
628 rattr.colors_per_channel = 2;
629
630 /* will only be accounted for in PseudoColor */
631 if (wPreferences.flags.create_stdcmap) {
632 rattr.standard_colormap_mode = RCreateStdColormap;
633 } else {
634 rattr.standard_colormap_mode = RUseStdColormap;
635 }
636
637 if (getWVisualID(screen_number) >= 0) {
638 rattr.flags |= RC_VisualID;
639 rattr.visualid = getWVisualID(screen_number);
640 }
641
642 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
643
644 if (!scr->rcontext && RErrorCode == RERR_STDCMAPFAIL) {
645 wwarning(RMessageForError(RErrorCode));
646
647 rattr.flags &= ~RC_StandardColormap;
648 rattr.standard_colormap_mode = RUseStdColormap;
649
650 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
651 }
652
653 if (!scr->rcontext) {
654 wwarning(_("could not initialize graphics library context: %s"), RMessageForError(RErrorCode));
655 wAbort(False);
656 } else {
657 char **formats;
658 int i = 0;
659
660 formats = RSupportedFileFormats();
661 if (formats) {
662 for (i = 0; formats[i] != NULL; i++) {
663 if (strcmp(formats[i], "TIFF") == 0) {
664 scr->flags.supports_tiff = 1;
665 break;
666 }
667 }
668 }
669 }
670
671 scr->w_win = scr->rcontext->drawable;
672 scr->w_visual = scr->rcontext->visual;
673 scr->w_depth = scr->rcontext->depth;
674 scr->w_colormap = scr->rcontext->cmap;
675
676 /* create screen descriptor for WINGs */
677 scr->wmscreen = WMCreateScreenWithRContext(dpy, screen_number, scr->rcontext);
678
679 if (!scr->wmscreen) {
680 wfatal(_("could not initialize WINGs widget set"));
681 return NULL;
682 }
683
684 scr->black = WMBlackColor(scr->wmscreen);
685 scr->white = WMWhiteColor(scr->wmscreen);
686 scr->gray = WMGrayColor(scr->wmscreen);
687 scr->darkGray = WMDarkGrayColor(scr->wmscreen);
688
689 scr->black_pixel = WMColorPixel(scr->black); /*scr->rcontext->black; */
690 scr->white_pixel = WMColorPixel(scr->white); /*scr->rcontext->white; */
691 scr->light_pixel = WMColorPixel(scr->gray);
692 scr->dark_pixel = WMColorPixel(scr->darkGray);
693
694 {
695 XColor xcol;
696 /* frame boder color */
697 wGetColor(scr, FRAME_BORDER_COLOR, &xcol);
698 scr->frame_border_pixel = xcol.pixel;
699 }
700
701 /* create GCs with default values */
702 allocGCs(scr);
703
704 /* for our window manager info notice board. Need to
705 * create before reading the defaults, because it will be used there.
706 */
707 scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, 0, 0);
708
709 /* read defaults for this screen */
710 wReadDefaults(scr, WDWindowMaker->dictionary);
711
712 createInternalWindows(scr);
713
714 wNETWMInitStuff(scr);
715
716 /* create initial workspace */
717 wWorkspaceNew(scr);
718
719 /* create shared pixmaps */
720 createPixmaps(scr);
721
722 /* set icon sizes we can accept from clients */
723 icon_size[0].min_width = 8;
724 icon_size[0].min_height = 8;
725 icon_size[0].max_width = wPreferences.icon_size - 4;
726 icon_size[0].max_height = wPreferences.icon_size - 4;
727 icon_size[0].width_inc = 1;
728 icon_size[0].height_inc = 1;
729 XSetIconSizes(dpy, scr->root_win, icon_size, 1);
730
731 /* setup WindowMaker protocols property in the root window */
732 PropSetWMakerProtocols(scr->root_win);
733
734 /* setup our noticeboard */
735 XChangeProperty(dpy, scr->info_window, _XA_WINDOWMAKER_NOTICEBOARD,
736 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
737 XChangeProperty(dpy, scr->root_win, _XA_WINDOWMAKER_NOTICEBOARD,
738 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
739
740 #ifdef BALLOON_TEXT
741 /* initialize balloon text stuff */
742 wBalloonInitialize(scr);
743 #endif
744
745 scr->info_text_font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
746
747 scr->tech_draw_font = XLoadQueryFont(dpy, "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*");
748 if (!scr->tech_draw_font)
749 scr->tech_draw_font = XLoadQueryFont(dpy, "fixed");
750
751 scr->gview = WCreateGeometryView(scr->wmscreen);
752 WMRealizeWidget(scr->gview);
753
754 wScreenUpdateUsableArea(scr);
755
756 return scr;
757 }
758
759 void wScreenUpdateUsableArea(WScreen * scr)
760 {
761 /*
762 * scr->totalUsableArea[] will become the usableArea used for Windowplacement,
763 * scr->usableArea[] will be used for iconplacement, hence no iconyard nor
764 * border.
765 */
766
767 int i;
768 unsigned long best_area = 0, tmp_area;
769 WArea area;
770 int dock_head = scr->xine_info.primary_head;
771
772 if (scr->dock) {
773 WMRect rect;
774 rect.pos.x = scr->dock->x_pos;
775 rect.pos.y = scr->dock->y_pos;
776 rect.size.width = wPreferences.icon_size;
777 rect.size.height = wPreferences.icon_size;
778 dock_head = wGetHeadForRect(scr, rect);
779 }
780
781 for (i = 0; i < wXineramaHeads(scr); ++i) {
782 WMRect rect = wGetRectForHead(scr, i);
783 scr->totalUsableArea[i].x1 = rect.pos.x;
784 scr->totalUsableArea[i].y1 = rect.pos.y;
785 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
786 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
787
788 if (scr->dock && dock_head == i && (!scr->dock->lowered || wPreferences.no_window_over_dock)) {
789 int offset = wPreferences.icon_size + DOCK_EXTRA_SPACE;
790
791 if (scr->dock->on_right_side) {
792 scr->totalUsableArea[i].x2 -= offset;
793 } else {
794 scr->totalUsableArea[i].x1 += offset;
795 }
796 }
797
798 {
799 WArea area;
800 if (wNETWMGetUsableArea(scr, i, &area)) {
801 scr->totalUsableArea[i].x1 = WMAX(scr->totalUsableArea[i].x1, area.x1);
802 scr->totalUsableArea[i].y1 = WMAX(scr->totalUsableArea[i].y1, area.y1);
803 scr->totalUsableArea[i].x2 = WMIN(scr->totalUsableArea[i].x2, area.x2);
804 scr->totalUsableArea[i].y2 = WMIN(scr->totalUsableArea[i].y2, area.y2);
805 }
806 }
807
808 scr->usableArea[i] = scr->totalUsableArea[i];
809
810 #if 0
811 printf("usableArea[%d]: %d %d %d %d\n", i,
812 scr->usableArea[i].x1, scr->usableArea[i].x2, scr->usableArea[i].y1, scr->usableArea[i].y2);
813 #endif
814
815 if (wPreferences.no_window_over_icons) {
816 if (wPreferences.icon_yard & IY_VERT) {
817 if (wPreferences.icon_yard & IY_RIGHT) {
818 scr->totalUsableArea[i].x2 -= wPreferences.icon_size;
819 } else {
820 scr->totalUsableArea[i].x1 += wPreferences.icon_size;
821 }
822 } else {
823 if (wPreferences.icon_yard & IY_TOP) {
824 scr->totalUsableArea[i].y1 += wPreferences.icon_size;
825 } else {
826 scr->totalUsableArea[i].y2 -= wPreferences.icon_size;
827 }
828 }
829 }
830
831 if (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1 < rect.size.width / 2) {
832 scr->totalUsableArea[i].x1 = rect.pos.x;
833 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
834 }
835
836 if (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1 < rect.size.height / 2) {
837 scr->totalUsableArea[i].y1 = rect.pos.y;
838 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
839 }
840
841 tmp_area = (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1) *
842 (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1);
843
844 if (tmp_area > best_area) {
845 best_area = tmp_area;
846 area = scr->totalUsableArea[i];
847 }
848
849 {
850 unsigned size = wPreferences.workspace_border_size;
851 unsigned position = wPreferences.workspace_border_position;
852
853 if (size > 0 && position != WB_NONE) {
854 if (position & WB_LEFTRIGHT) {
855 scr->totalUsableArea[i].x1 += size;
856 scr->totalUsableArea[i].x2 -= size;
857 }
858 if (position & WB_TOPBOTTOM) {
859 scr->totalUsableArea[i].y1 += size;
860 scr->totalUsableArea[i].y2 -= size;
861 }
862 }
863 }
864 }
865
866 wNETWMUpdateWorkarea(scr, area);
867
868 if (wPreferences.auto_arrange_icons)
869 wArrangeIcons(scr, True);
870 }
871
872 void wScreenRestoreState(WScreen * scr)
873 {
874 WMPropList *state;
875 char *path;
876
877 OpenRootMenu(scr, -10000, -10000, False);
878 wMenuUnmap(scr->root_menu);
879
880 make_keys();
881
882 if (wScreenCount == 1) {
883 path = wdefaultspathfordomain("WMState");
884 } else {
885 char buf[16];
886 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
887 path = wdefaultspathfordomain(buf);
888 }
889 scr->session_state = WMReadPropListFromFile(path);
890 wfree(path);
891 if (!scr->session_state && wScreenCount > 1) {
892 path = wdefaultspathfordomain("WMState");
893 scr->session_state = WMReadPropListFromFile(path);
894 wfree(path);
895 }
896
897 if (!scr->session_state) {
898 scr->session_state = WMCreatePLDictionary(NULL, NULL);
899 }
900
901 if (!wPreferences.flags.nodock) {
902 state = WMGetFromPLDictionary(scr->session_state, dDock);
903 scr->dock = wDockRestoreState(scr, state, WM_DOCK);
904 }
905
906 if (!wPreferences.flags.noclip) {
907 state = WMGetFromPLDictionary(scr->session_state, dClip);
908 scr->clip_icon = wClipRestoreState(scr, state);
909 }
910
911 wWorkspaceRestoreState(scr);
912
913 wScreenUpdateUsableArea(scr);
914 }
915
916 void wScreenSaveState(WScreen * scr)
917 {
918 WWindow *wwin;
919 char *str;
920 WMPropList *old_state, *foo;
921
922 make_keys();
923
924 /* save state of windows */
925 wwin = scr->focused_window;
926 while (wwin) {
927 wWindowSaveState(wwin);
928 wwin = wwin->prev;
929 }
930
931 if (wPreferences.flags.noupdates)
932 return;
933
934 old_state = scr->session_state;
935 scr->session_state = WMCreatePLDictionary(NULL, NULL);
936
937 WMPLSetCaseSensitive(True);
938
939 /* save dock state to file */
940 if (!wPreferences.flags.nodock) {
941 wDockSaveState(scr, old_state);
942 } else {
943 if ((foo = WMGetFromPLDictionary(old_state, dDock)) != NULL) {
944 WMPutInPLDictionary(scr->session_state, dDock, foo);
945 }
946 }
947 if (!wPreferences.flags.noclip) {
948 wClipSaveState(scr);
949 } else {
950 if ((foo = WMGetFromPLDictionary(old_state, dClip)) != NULL) {
951 WMPutInPLDictionary(scr->session_state, dClip, foo);
952 }
953 }
954
955 wWorkspaceSaveState(scr, old_state);
956
957 if (wPreferences.save_session_on_exit) {
958 wSessionSaveState(scr);
959 } else {
960 if ((foo = WMGetFromPLDictionary(old_state, dApplications)) != NULL) {
961 WMPutInPLDictionary(scr->session_state, dApplications, foo);
962 }
963 if ((foo = WMGetFromPLDictionary(old_state, dWorkspace)) != NULL) {
964 WMPutInPLDictionary(scr->session_state, dWorkspace, foo);
965 }
966 }
967
968 /* clean up */
969 WMPLSetCaseSensitive(False);
970
971 wMenuSaveState(scr);
972
973 if (wScreenCount == 1) {
974 str = wdefaultspathfordomain("WMState");
975 } else {
976 char buf[16];
977 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
978 str = wdefaultspathfordomain(buf);
979 }
980 if (!WMWritePropListToFile(scr->session_state, str)) {
981 wsyserror(_("could not save session state in %s"), str);
982 }
983 wfree(str);
984 WMReleasePropList(old_state);
985 }
986
987 int wScreenBringInside(WScreen * scr, int *x, int *y, int width, int height)
988 {
989 int moved = 0;
990 int tol_w, tol_h;
991 /*
992 * With respect to the head that contains most of the window.
993 */
994 int sx1, sy1, sx2, sy2;
995
996 WMRect rect;
997 int head, flags;
998
999 rect.pos.x = *x;
1000 rect.pos.y = *y;
1001 rect.size.width = width;
1002 rect.size.height = height;
1003
1004 head = wGetRectPlacementInfo(scr, rect, &flags);
1005 rect = wGetRectForHead(scr, head);
1006
1007 sx1 = rect.pos.x;
1008 sy1 = rect.pos.y;
1009 sx2 = sx1 + rect.size.width;
1010 sy2 = sy1 + rect.size.height;
1011
1012 #if 0 /* NOTE: gives funky group movement */
1013 if (flags & XFLAG_MULTIPLE) {
1014 /*
1015 * since we span multiple heads, pull window totaly inside
1016 */
1017 if (*x < sx1)
1018 *x = sx1, moved = 1;
1019 else if (*x + width > sx2)
1020 *x = sx2 - width, moved = 1;
1021
1022 if (*y < sy1)
1023 *y = sy1, moved = 1;
1024 else if (*y + height > sy2)
1025 *y = sy2 - height, moved = 1;
1026
1027 return moved;
1028 }
1029 #endif
1030
1031 if (width > 20)
1032 tol_w = width / 2;
1033 else
1034 tol_w = 20;
1035
1036 if (height > 20)
1037 tol_h = height / 2;
1038 else
1039 tol_h = 20;
1040
1041 if (*x + width < sx1 + 10)
1042 *x = sx1 - tol_w, moved = 1;
1043 else if (*x >= sx2 - 10)
1044 *x = sx2 - tol_w - 1, moved = 1;
1045
1046 if (*y < sy1 - height + 10)
1047 *y = sy1 - tol_h, moved = 1;
1048 else if (*y >= sy2 - 10)
1049 *y = sy2 - tol_h - 1, moved = 1;
1050
1051 return moved;
1052 }
1053
1054 int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height)
1055 {
1056 int moved = 0;
1057 int sx1, sy1, sx2, sy2;
1058 WMRect rect;
1059 int head;
1060
1061 rect.pos.x = *x;
1062 rect.pos.y = *y;
1063 rect.size.width = width;
1064 rect.size.height = height;
1065
1066 head = wGetHeadForRect(scr, rect);
1067 rect = wGetRectForHead(scr, head);
1068
1069 sx1 = rect.pos.x;
1070 sy1 = rect.pos.y;
1071 sx2 = sx1 + rect.size.width;
1072 sy2 = sy1 + rect.size.height;
1073
1074 if (*x < sx1)
1075 *x = sx1, moved = 1;
1076 else if (*x + width > sx2)
1077 *x = sx2 - width, moved = 1;
1078
1079 if (*y < sy1)
1080 *y = sy1, moved = 1;
1081 else if (*y + height > sy2)
1082 *y = sy2 - height, moved = 1;
1083
1084 return moved;
1085 }