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 }