debian: Import version 0.95.6-1.
[wmaker-crm.git] / src / screen.c
blob01571c008e15a8d51387549e3ad0a876faedee23
1 /* screen.c - screen management
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
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.
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.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "wconfig.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include <X11/Xatom.h>
31 #ifdef KEEP_XKB_LOCK_STATUS
32 #include <X11/XKBlib.h>
33 #endif /* KEEP_XKB_LOCK_STATUS */
34 #ifdef USE_RANDR
35 #include <X11/extensions/Xrandr.h>
36 #endif
38 #include <wraster.h>
39 #include "WindowMaker.h"
40 #include "def_pixmaps.h"
41 #include "screen.h"
42 #include "texture.h"
43 #include "pixmap.h"
44 #include "menu.h"
45 #include "window.h"
46 #include "main.h"
47 #include "actions.h"
48 #include "properties.h"
49 #include "dock.h"
50 #include "resources.h"
51 #include "workspace.h"
52 #include "session.h"
53 #include "balloon.h"
54 #include "geomview.h"
55 #include "wmspec.h"
56 #include "rootmenu.h"
58 #include "xinerama.h"
60 #include <WINGs/WUtil.h>
62 #include "defaults.h"
64 #define EVENT_MASK (LeaveWindowMask|EnterWindowMask|PropertyChangeMask\
65 |SubstructureNotifyMask|PointerMotionMask \
66 |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
67 |KeyPressMask|KeyReleaseMask)
69 #ifdef USE_ICCCM_WMREPLACE
70 #define REPLACE_WM_TIMEOUT 15
71 #endif
73 #define STIPPLE_WIDTH 2
74 #define STIPPLE_HEIGHT 2
75 static char STIPPLE_DATA[] = { 0x02, 0x01 };
77 static int CantManageScreen = 0;
79 static WMPropList *dApplications = NULL;
80 static WMPropList *dWorkspace;
81 static WMPropList *dDock;
82 static WMPropList *dClip;
83 static WMPropList *dDrawers = NULL;
85 static void make_keys(void)
87 if (dApplications != NULL)
88 return;
90 dApplications = WMCreatePLString("Applications");
91 dWorkspace = WMCreatePLString("Workspace");
92 dDock = WMCreatePLString("Dock");
93 dClip = WMCreatePLString("Clip");
94 dDrawers = WMCreatePLString("Drawers");
98 * Support for ICCCM 2.0: Window Manager Replacement protocol
99 * See: http://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
101 * Basically, user should be able to dynamically change its window manager; this is done
102 * cooperatively through a special Selection ("WM_Sn" where 'n' is the X screen number)
104 * This function does 2 things:
105 * - it checks if this selection is already owned, which means that another window
106 * manager is running. If it is the case and user specified '--replace' on the command
107 * line, then it asks the WM to shut down;
108 * - when ok, it sets the selection ownership to ourself, so another window manager
109 * may ask us to shut down (this is handled in "event.c")
111 static Bool replace_existing_wm(WScreen *scr)
113 #ifdef USE_ICCCM_WMREPLACE
114 char atomName[16];
115 Window wm;
116 XSetWindowAttributes attribs;
117 XClientMessageEvent event;
118 unsigned long current_time;
119 int ret;
121 /* Try to acquire the atom named WM_S<screen> */
122 ret = snprintf(atomName, sizeof(atomName), "WM_S%d", scr->screen);
123 if (ret < 0 || ret == sizeof(atomName)) {
124 werror("out of memory trying to allocate window manager selection atom for screen %d", scr->screen);
125 return False;
128 scr->sn_atom = XInternAtom(dpy, atomName, False);
129 if (! scr->sn_atom)
130 return False;
132 /* Check if an existing window manager owns the selection */
133 wm = XGetSelectionOwner(dpy, scr->sn_atom);
134 if (wm) {
135 if (!wPreferences.flags.replace) {
136 wmessage(_("another window manager is running"));
137 wwarning(_("use the --replace flag to replace it"));
138 return False;
141 attribs.event_mask = StructureNotifyMask;
142 if (!XChangeWindowAttributes(dpy, wm, CWEventMask, &attribs))
143 wm = None;
145 #endif
147 /* for our window manager info notice board and the selection owner */
148 scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, 0, 0);
150 #ifdef USE_ICCCM_WMREPLACE
151 /* Try to acquire the selection */
152 current_time = CurrentTime;
153 ret = XSetSelectionOwner(dpy, scr->sn_atom, scr->info_window, current_time);
154 if (ret == BadAtom || ret == BadWindow)
155 return False;
157 /* Wait for other window manager to exit */
158 if (wm) {
159 unsigned long wait = 0;
160 unsigned long timeout = REPLACE_WM_TIMEOUT * 1000000L;
161 XEvent event;
163 while (wait < timeout) {
164 if (!(wait % 1000000))
165 wmessage(_("waiting %lus for other window manager to exit"), (timeout - wait) / 1000000L);
167 if (XCheckWindowEvent(dpy, wm, StructureNotifyMask, &event))
168 if (event.type == DestroyNotify)
169 break;
171 wusleep(100000);
172 wait += 100000;
175 if (wait >= timeout) {
176 wwarning(_("other window manager hasn't exited!"));
177 return False;
180 wmessage(_("replacing the other window manager"));
183 if (XGetSelectionOwner(dpy, scr->sn_atom) != scr->info_window)
184 return False;
186 event.type = ClientMessage;
187 event.message_type = scr->sn_atom;
188 event.format = 32;
189 event.data.l[0] = (long) current_time;
190 event.data.l[1] = (long) scr->sn_atom;
191 event.data.l[2] = (long) scr->info_window;
192 event.data.l[3] = (long) 0L;
193 event.data.l[4] = (long) 0L;
194 event.window = scr->root_win;
195 XSendEvent(dpy, scr->root_win, False, StructureNotifyMask, (XEvent *) &event);
196 #endif
198 return True;
202 *----------------------------------------------------------------------
203 * alreadyRunningError--
204 * X error handler used to catch errors when trying to do
205 * XSelectInput() on the root window. These errors probably mean that
206 * there already is some other window manager running.
208 * Returns:
209 * Nothing, unless something really evil happens...
211 * Side effects:
212 * CantManageScreen is set to 1;
213 *----------------------------------------------------------------------
215 static int alreadyRunningError(Display * dpy, XErrorEvent * error)
217 /* Parameter not used, but tell the compiler that it is ok */
218 (void) dpy;
219 (void) error;
221 CantManageScreen = 1;
222 return -1;
226 *----------------------------------------------------------------------
227 * allocButtonPixmaps--
228 * Allocate pixmaps used on window operation buttons (those in the
229 * titlebar). The pixmaps are linked to the program. If XPM is supported
230 * XPM pixmaps are used otherwise, equivalent bitmaps are used.
232 * Returns:
233 * Nothing
235 * Side effects:
236 * Allocates shared pixmaps for the screen. These pixmaps should
237 * not be freed by anybody.
238 *----------------------------------------------------------------------
240 static void allocButtonPixmaps(WScreen * scr)
242 WPixmap *pix;
244 /* create predefined pixmaps */
245 if (wPreferences.new_style == TS_NEXT) {
246 pix = wPixmapCreateFromXPMData(scr, NEXT_CLOSE_XPM);
247 } else {
248 pix = wPixmapCreateFromXPMData(scr, PRED_CLOSE_XPM);
250 if (pix)
251 pix->shared = 1;
252 scr->b_pixmaps[WBUT_CLOSE] = pix;
254 if (wPreferences.new_style == TS_NEXT) {
255 pix = wPixmapCreateFromXPMData(scr, NEXT_BROKEN_CLOSE_XPM);
256 } else {
257 pix = wPixmapCreateFromXPMData(scr, PRED_BROKEN_CLOSE_XPM);
259 if (pix)
260 pix->shared = 1;
261 scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;
263 if (wPreferences.new_style == TS_NEXT) {
264 pix = wPixmapCreateFromXPMData(scr, NEXT_ICONIFY_XPM);
265 } else {
266 pix = wPixmapCreateFromXPMData(scr, PRED_ICONIFY_XPM);
268 if (pix)
269 pix->shared = 1;
270 scr->b_pixmaps[WBUT_ICONIFY] = pix;
271 #ifdef XKB_BUTTON_HINT
272 if (wPreferences.new_style == TS_NEXT) {
273 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP1_XPM);
274 } else {
275 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP1_XPM);
277 if (pix)
278 pix->shared = 1;
279 scr->b_pixmaps[WBUT_XKBGROUP1] = pix;
280 if (wPreferences.new_style == TS_NEXT) {
281 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP2_XPM);
282 } else {
283 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP2_XPM);
285 if (pix)
286 pix->shared = 1;
287 scr->b_pixmaps[WBUT_XKBGROUP2] = pix;
288 if (wPreferences.new_style == TS_NEXT) {
289 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP3_XPM);
290 } else {
291 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP3_XPM);
293 if (pix)
294 pix->shared = 1;
295 scr->b_pixmaps[WBUT_XKBGROUP3] = pix;
296 if (wPreferences.new_style == TS_NEXT) {
297 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP4_XPM);
298 } else {
299 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP4_XPM);
301 if (pix)
302 pix->shared = 1;
303 scr->b_pixmaps[WBUT_XKBGROUP4] = pix;
304 #endif
306 if (wPreferences.new_style == TS_NEXT) {
307 pix = wPixmapCreateFromXPMData(scr, NEXT_KILL_XPM);
308 } else {
309 pix = wPixmapCreateFromXPMData(scr, PRED_KILL_XPM);
311 if (pix)
312 pix->shared = 1;
313 scr->b_pixmaps[WBUT_KILL] = pix;
316 static void draw_dot(WScreen * scr, Drawable d, int x, int y, GC gc)
318 XSetForeground(dpy, gc, scr->black_pixel);
319 XDrawLine(dpy, d, gc, x, y, x + 1, y);
320 XDrawPoint(dpy, d, gc, x, y + 1);
321 XSetForeground(dpy, gc, scr->white_pixel);
322 XDrawLine(dpy, d, gc, x + 2, y, x + 2, y + 1);
323 XDrawPoint(dpy, d, gc, x + 1, y + 1);
326 static WPixmap *make3Dots(WScreen * scr)
328 WPixmap *wpix;
329 GC gc2, gc;
330 XGCValues gcv;
331 Pixmap pix, mask;
333 gc = scr->copy_gc;
334 pix = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth);
335 XSetForeground(dpy, gc, scr->black_pixel);
336 XFillRectangle(dpy, pix, gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
337 XSetForeground(dpy, gc, scr->white_pixel);
338 draw_dot(scr, pix, 4, wPreferences.icon_size - 6, gc);
339 draw_dot(scr, pix, 9, wPreferences.icon_size - 6, gc);
340 draw_dot(scr, pix, 14, wPreferences.icon_size - 6, gc);
342 mask = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, 1);
343 gcv.foreground = 0;
344 gcv.graphics_exposures = False;
345 gc2 = XCreateGC(dpy, mask, GCForeground | GCGraphicsExposures, &gcv);
346 XFillRectangle(dpy, mask, gc2, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
347 XSetForeground(dpy, gc2, 1);
348 XFillRectangle(dpy, mask, gc2, 4, wPreferences.icon_size - 6, 3, 2);
349 XFillRectangle(dpy, mask, gc2, 9, wPreferences.icon_size - 6, 3, 2);
350 XFillRectangle(dpy, mask, gc2, 14, wPreferences.icon_size - 6, 3, 2);
352 XFreeGC(dpy, gc2);
354 wpix = wPixmapCreate(pix, mask);
355 wpix->shared = 1;
357 return wpix;
360 static void allocGCs(WScreen * scr)
362 XGCValues gcv;
363 XColor color;
364 int gcm;
366 scr->stipple_bitmap = XCreateBitmapFromData(dpy, scr->w_win, STIPPLE_DATA, STIPPLE_WIDTH, STIPPLE_HEIGHT);
368 gcv.stipple = scr->stipple_bitmap;
369 gcv.foreground = scr->white_pixel;
370 gcv.fill_style = FillStippled;
371 gcv.graphics_exposures = False;
372 gcm = GCForeground | GCStipple | GCFillStyle | GCGraphicsExposures;
373 scr->stipple_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
375 /* selected icon border GCs */
376 gcv.function = GXcopy;
377 gcv.foreground = scr->white_pixel;
378 gcv.background = scr->black_pixel;
379 gcv.line_width = 1;
380 gcv.line_style = LineDoubleDash;
381 gcv.fill_style = FillSolid;
382 gcv.dash_offset = 0;
383 gcv.dashes = 4;
384 gcv.graphics_exposures = False;
386 gcm = GCFunction | GCGraphicsExposures;
387 gcm |= GCForeground | GCBackground;
388 gcm |= GCLineWidth | GCLineStyle;
389 gcm |= GCFillStyle;
390 gcm |= GCDashOffset | GCDashList;
392 scr->icon_select_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
394 scr->menu_title_color[0] = WMRetainColor(scr->white);
396 /* don't retain scr->black here because we may alter its alpha */
397 scr->mtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
398 scr->dtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
400 /* frame GC */
401 wGetColor(scr, DEF_FRAME_COLOR, &color);
402 gcv.function = GXxor;
403 /* this will raise the probability of the XORed color being different
404 * of the original color in PseudoColor when not all color cells are
405 * initialized */
406 if (DefaultVisual(dpy, scr->screen)->class == PseudoColor)
407 gcv.plane_mask = (1 << (scr->depth - 1)) | 1;
408 else
409 gcv.plane_mask = AllPlanes;
410 gcv.foreground = color.pixel;
411 if (gcv.foreground == 0)
412 gcv.foreground = 1;
413 gcv.line_width = DEF_FRAME_THICKNESS;
414 gcv.subwindow_mode = IncludeInferiors;
415 gcv.graphics_exposures = False;
416 scr->frame_gc = XCreateGC(dpy, scr->root_win, GCForeground | GCGraphicsExposures
417 | GCFunction | GCSubwindowMode | GCLineWidth | GCPlaneMask, &gcv);
419 /* line GC */
420 gcv.foreground = color.pixel;
422 if (gcv.foreground == 0)
423 /* XOR:ing with a zero is not going to be of much use, so
424 in that case, we somewhat arbitrarily xor with 17 instead. */
425 gcv.foreground = 17;
427 gcv.function = GXxor;
428 gcv.subwindow_mode = IncludeInferiors;
429 gcv.line_width = 1;
430 gcv.cap_style = CapRound;
431 gcv.graphics_exposures = False;
432 gcm = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth | GCCapStyle | GCGraphicsExposures;
433 scr->line_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);
435 scr->line_pixel = gcv.foreground;
437 /* copy GC */
438 gcv.foreground = scr->white_pixel;
439 gcv.background = scr->black_pixel;
440 gcv.graphics_exposures = False;
441 scr->copy_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
443 /* misc drawing GC */
444 gcv.graphics_exposures = False;
445 gcm = GCGraphicsExposures;
446 scr->draw_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
448 assert(scr->stipple_bitmap != None);
450 /* mono GC */
451 scr->mono_gc = XCreateGC(dpy, scr->stipple_bitmap, gcm, &gcv);
454 static void createPixmaps(WScreen * scr)
456 WPixmap *pix;
458 /* load pixmaps */
459 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_RADIO_INDICATOR_XBM_DATA,
460 (char *)MENU_RADIO_INDICATOR_XBM_DATA,
461 MENU_RADIO_INDICATOR_XBM_SIZE,
462 MENU_RADIO_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
463 if (pix != NULL)
464 pix->shared = 1;
465 scr->menu_radio_indicator = pix;
467 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_CHECK_INDICATOR_XBM_DATA,
468 (char *)MENU_CHECK_INDICATOR_XBM_DATA,
469 MENU_CHECK_INDICATOR_XBM_SIZE,
470 MENU_CHECK_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
471 if (pix != NULL)
472 pix->shared = 1;
473 scr->menu_check_indicator = pix;
475 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_MINI_INDICATOR_XBM_DATA,
476 (char *)MENU_MINI_INDICATOR_XBM_DATA,
477 MENU_MINI_INDICATOR_XBM_SIZE,
478 MENU_MINI_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
479 if (pix != NULL)
480 pix->shared = 1;
481 scr->menu_mini_indicator = pix;
483 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_HIDE_INDICATOR_XBM_DATA,
484 (char *)MENU_HIDE_INDICATOR_XBM_DATA,
485 MENU_HIDE_INDICATOR_XBM_SIZE,
486 MENU_HIDE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
487 if (pix != NULL)
488 pix->shared = 1;
489 scr->menu_hide_indicator = pix;
491 pix = wPixmapCreateFromXBMData(scr, (char *)MENU_SHADE_INDICATOR_XBM_DATA,
492 (char *)MENU_SHADE_INDICATOR_XBM_DATA,
493 MENU_SHADE_INDICATOR_XBM_SIZE,
494 MENU_SHADE_INDICATOR_XBM_SIZE, scr->black_pixel, scr->white_pixel);
495 if (pix != NULL)
496 pix->shared = 1;
497 scr->menu_shade_indicator = pix;
499 create_logo_image(scr);
501 scr->dock_dots = make3Dots(scr);
503 /* titlebar button pixmaps */
504 allocButtonPixmaps(scr);
507 void create_logo_image(WScreen *scr)
509 RImage *image = get_icon_image(scr, "Logo", "WMPanel", 128);
511 if (!image) {
512 wwarning(_("could not load logo image for panels: %s"), RMessageForError(RErrorCode));
513 } else {
514 WMSetApplicationIconImage(scr->wmscreen, image);
515 RReleaseImage(image);
520 *----------------------------------------------------------------------
521 * createInternalWindows--
522 * Creates some windows used internally by the program. One to
523 * receive input focus when no other window can get it and another
524 * to display window geometry information during window resize/move.
526 * Returns:
527 * Nothing
529 * Side effects:
530 * Windows are created and some colors are allocated for the
531 * window background.
532 *----------------------------------------------------------------------
534 static void createInternalWindows(WScreen * scr)
536 int vmask;
537 XSetWindowAttributes attribs;
539 /* InputOnly window to get the focus when no other window can get it */
540 vmask = CWEventMask | CWOverrideRedirect;
541 attribs.event_mask = KeyPressMask | FocusChangeMask;
542 attribs.override_redirect = True;
543 scr->no_focus_win = XCreateWindow(dpy, scr->root_win, -10, -10, 4, 4, 0, 0,
544 InputOnly, CopyFromParent, vmask, &attribs);
545 XSelectInput(dpy, scr->no_focus_win, KeyPressMask | KeyReleaseMask);
546 XMapWindow(dpy, scr->no_focus_win);
548 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
550 /* shadow window for dock buttons */
551 vmask = CWBorderPixel | CWBackPixmap | CWBackPixel | CWCursor | CWSaveUnder | CWOverrideRedirect;
552 attribs.border_pixel = scr->black_pixel;
553 attribs.save_under = True;
554 attribs.override_redirect = True;
555 attribs.background_pixmap = None;
556 attribs.background_pixel = scr->white_pixel;
557 attribs.cursor = wPreferences.cursor[WCUR_NORMAL];
558 vmask |= CWColormap;
559 attribs.colormap = scr->w_colormap;
560 scr->dock_shadow =
561 XCreateWindow(dpy, scr->root_win, 0, 0, wPreferences.icon_size,
562 wPreferences.icon_size, 0, scr->w_depth, CopyFromParent, scr->w_visual, vmask, &attribs);
564 /* workspace name */
565 vmask = CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWColormap | CWBorderPixel;
566 attribs.save_under = True;
567 attribs.override_redirect = True;
568 attribs.colormap = scr->w_colormap;
569 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
570 attribs.border_pixel = 0; /* do not care */
571 scr->workspace_name =
572 XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
573 CopyFromParent, scr->w_visual, vmask, &attribs);
577 *----------------------------------------------------------------------
578 * wScreenInit--
579 * Initializes the window manager for the given screen and
580 * allocates a WScreen descriptor for it. Many resources are allocated
581 * for the screen and the root window is setup appropriately.
583 * Returns:
584 * The WScreen descriptor for the screen.
586 * Side effects:
587 * Many resources are allocated and the IconSize property is
588 * set on the root window.
589 * The program can be aborted if some fatal error occurs.
591 * TODO: User specifiable visual.
592 *----------------------------------------------------------------------
594 WScreen *wScreenInit(int screen_number)
596 WScreen *scr;
597 XIconSize icon_size[1];
598 RContextAttributes rattr;
599 long event_mask;
600 XErrorHandler oldHandler;
601 int i;
603 scr = wmalloc(sizeof(WScreen));
605 scr->stacking_list = WMCreateTreeBag();
607 /* initialize globals */
608 scr->screen = screen_number;
609 scr->root_win = RootWindow(dpy, screen_number);
610 scr->depth = DefaultDepth(dpy, screen_number);
611 scr->colormap = DefaultColormap(dpy, screen_number);
613 scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
614 scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
616 wInitXinerama(scr);
618 scr->usableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
619 scr->totalUsableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
621 for (i = 0; i < wXineramaHeads(scr); ++i) {
622 WMRect rect = wGetRectForHead(scr, i);
623 scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = rect.pos.x;
624 scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = rect.pos.y;
625 scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
626 scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
629 scr->fakeGroupLeaders = WMCreateArray(16);
631 CantManageScreen = 0;
632 oldHandler = XSetErrorHandler(alreadyRunningError);
634 /* Do we need to replace an existing window manager? */
635 if (!replace_existing_wm(scr)) {
636 XDestroyWindow(dpy, scr->info_window);
637 wfree(scr);
638 return NULL;
641 event_mask = EVENT_MASK;
642 XSelectInput(dpy, scr->root_win, event_mask);
644 #ifdef KEEP_XKB_LOCK_STATUS
645 /* Only GroupLock doesn't work correctly in my system since right-alt
646 * can change mode while holding it too - ]d
648 if (w_global.xext.xkb.supported) {
649 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask);
651 #endif /* KEEP_XKB_LOCK_STATUS */
653 #ifdef USE_RANDR
654 if (w_global.xext.randr.supported)
655 XRRSelectInput(dpy, scr->root_win, RRScreenChangeNotifyMask);
656 #endif
658 XSync(dpy, False);
659 XSetErrorHandler(oldHandler);
661 if (CantManageScreen) {
662 wfree(scr);
663 return NULL;
666 XDefineCursor(dpy, scr->root_win, wPreferences.cursor[WCUR_ROOT]);
668 /* screen descriptor for raster graphic library */
669 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap;
670 rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering;
672 /* if the std colormap stuff works ok, this will be ignored */
673 rattr.colors_per_channel = wPreferences.cmap_size;
674 if (rattr.colors_per_channel < 2)
675 rattr.colors_per_channel = 2;
677 /* Use standard colormap */
678 rattr.standard_colormap_mode = RUseStdColormap;
680 if (getWVisualID(screen_number) >= 0) {
681 rattr.flags |= RC_VisualID;
682 rattr.visualid = getWVisualID(screen_number);
685 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
687 if (!scr->rcontext && RErrorCode == RERR_STDCMAPFAIL) {
688 wwarning("%s", RMessageForError(RErrorCode));
690 rattr.flags &= ~RC_StandardColormap;
691 rattr.standard_colormap_mode = RUseStdColormap;
693 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
695 if (scr->rcontext == NULL) {
696 wfatal(_("can't create Context on screen %d, %s"),
697 screen_number, RMessageForError(RErrorCode));
698 goto abort_no_context;
701 scr->w_win = scr->rcontext->drawable;
702 scr->w_visual = scr->rcontext->visual;
703 scr->w_depth = scr->rcontext->depth;
704 scr->w_colormap = scr->rcontext->cmap;
706 /* create screen descriptor for WINGs */
707 scr->wmscreen = WMCreateScreenWithRContext(dpy, screen_number, scr->rcontext);
709 if (!scr->wmscreen) {
710 wfatal(_("could not initialize WINGs widget set"));
711 RDestroyContext(scr->rcontext);
712 abort_no_context:
713 WMFreeArray(scr->fakeGroupLeaders);
714 wfree(scr->totalUsableArea);
715 wfree(scr->usableArea);
716 WMFreeBag(scr->stacking_list);
717 wfree(scr);
718 return NULL;
721 scr->black = WMBlackColor(scr->wmscreen);
722 scr->white = WMWhiteColor(scr->wmscreen);
723 scr->gray = WMGrayColor(scr->wmscreen);
724 scr->darkGray = WMDarkGrayColor(scr->wmscreen);
726 scr->black_pixel = WMColorPixel(scr->black); /*scr->rcontext->black; */
727 scr->white_pixel = WMColorPixel(scr->white); /*scr->rcontext->white; */
728 scr->light_pixel = WMColorPixel(scr->gray);
729 scr->dark_pixel = WMColorPixel(scr->darkGray);
731 /* create GCs with default values */
732 allocGCs(scr);
734 /* read defaults for this screen */
735 wReadDefaults(scr, w_global.domain.wmaker->dictionary);
738 XColor xcol;
739 /* frame boder color */
740 wGetColor(scr, WMGetColorRGBDescription(scr->frame_border_color), &xcol);
741 scr->frame_border_pixel = xcol.pixel;
742 wGetColor(scr, WMGetColorRGBDescription(scr->frame_focused_border_color), &xcol);
743 scr->frame_focused_border_pixel = xcol.pixel;
744 wGetColor(scr, WMGetColorRGBDescription(scr->frame_selected_border_color), &xcol);
745 scr->frame_selected_border_pixel = xcol.pixel;
748 createInternalWindows(scr);
750 wNETWMInitStuff(scr);
752 /* create initial workspace */
753 wWorkspaceNew(scr);
755 /* create shared pixmaps */
756 createPixmaps(scr);
758 /* set icon sizes we can accept from clients */
759 icon_size[0].min_width = 8;
760 icon_size[0].min_height = 8;
761 icon_size[0].max_width = wPreferences.icon_size - 4;
762 icon_size[0].max_height = wPreferences.icon_size - 4;
763 icon_size[0].width_inc = 1;
764 icon_size[0].height_inc = 1;
765 XSetIconSizes(dpy, scr->root_win, icon_size, 1);
767 /* setup WindowMaker protocols property in the root window */
768 PropSetWMakerProtocols(scr->root_win);
770 /* setup our noticeboard */
771 XChangeProperty(dpy, scr->info_window, w_global.atom.wmaker.noticeboard,
772 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
773 XChangeProperty(dpy, scr->root_win, w_global.atom.wmaker.noticeboard,
774 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
776 #ifdef BALLOON_TEXT
777 /* initialize balloon text stuff */
778 wBalloonInitialize(scr);
779 #endif
781 scr->info_text_font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
783 scr->tech_draw_font = XLoadQueryFont(dpy, "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*");
784 if (!scr->tech_draw_font)
785 scr->tech_draw_font = XLoadQueryFont(dpy, "fixed");
787 scr->gview = WCreateGeometryView(scr->wmscreen);
788 WMRealizeWidget(scr->gview);
790 wScreenUpdateUsableArea(scr);
792 return scr;
795 void wScreenUpdateUsableArea(WScreen *scr)
798 * scr->totalUsableArea[] will become the usableArea used for Windowplacement,
799 * scr->usableArea[] will be used for iconplacement, hence no iconyard nor
800 * border.
803 WArea area;
804 int i;
805 unsigned long tmp_area, best_area = 0;
806 unsigned int size = wPreferences.workspace_border_size;
807 unsigned int position = wPreferences.workspace_border_position;
809 for (i = 0; i < wXineramaHeads(scr); ++i) {
810 WMRect rect = wGetRectForHead(scr, i);
811 scr->totalUsableArea[i].x1 = rect.pos.x;
812 scr->totalUsableArea[i].y1 = rect.pos.y;
813 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
814 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
816 if (wNETWMGetUsableArea(scr, i, &area)) {
817 scr->totalUsableArea[i].x1 = WMAX(scr->totalUsableArea[i].x1, area.x1);
818 scr->totalUsableArea[i].y1 = WMAX(scr->totalUsableArea[i].y1, area.y1);
819 scr->totalUsableArea[i].x2 = WMIN(scr->totalUsableArea[i].x2, area.x2);
820 scr->totalUsableArea[i].y2 = WMIN(scr->totalUsableArea[i].y2, area.y2);
823 scr->usableArea[i] = scr->totalUsableArea[i];
825 if (wPreferences.no_window_over_icons) {
826 if (wPreferences.icon_yard & IY_VERT) {
827 if (wPreferences.icon_yard & IY_RIGHT)
828 scr->totalUsableArea[i].x2 -= wPreferences.icon_size;
829 else
830 scr->totalUsableArea[i].x1 += wPreferences.icon_size;
831 } else {
832 if (wPreferences.icon_yard & IY_TOP)
833 scr->totalUsableArea[i].y1 += wPreferences.icon_size;
834 else
835 scr->totalUsableArea[i].y2 -= wPreferences.icon_size;
839 if (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1 < rect.size.width / 2) {
840 scr->totalUsableArea[i].x1 = rect.pos.x;
841 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
844 if (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1 < rect.size.height / 2) {
845 scr->totalUsableArea[i].y1 = rect.pos.y;
846 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
849 tmp_area = (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1) *
850 (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1);
852 if (tmp_area > best_area) {
853 best_area = tmp_area;
854 area = scr->totalUsableArea[i];
857 if (size > 0 && position != WB_NONE) {
858 if (position & WB_LEFTRIGHT) {
859 scr->totalUsableArea[i].x1 += size;
860 scr->totalUsableArea[i].x2 -= size;
862 if (position & WB_TOPBOTTOM) {
863 scr->totalUsableArea[i].y1 += size;
864 scr->totalUsableArea[i].y2 -= size;
869 if (wPreferences.auto_arrange_icons)
870 wArrangeIcons(scr, True);
873 void wScreenRestoreState(WScreen * scr)
875 WMPropList *state;
876 char *path;
878 OpenRootMenu(scr, -10000, -10000, False);
879 wMenuUnmap(scr->root_menu);
881 make_keys();
883 if (w_global.screen_count == 1) {
884 path = wdefaultspathfordomain("WMState");
885 } else {
886 char buf[16];
887 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
888 path = wdefaultspathfordomain(buf);
890 scr->session_state = WMReadPropListFromFile(path);
891 wfree(path);
892 if (!scr->session_state && w_global.screen_count > 1) {
893 path = wdefaultspathfordomain("WMState");
894 scr->session_state = WMReadPropListFromFile(path);
895 wfree(path);
898 if (!scr->session_state)
899 scr->session_state = WMCreatePLDictionary(NULL, NULL);
901 if (!wPreferences.flags.nodock) {
902 state = WMGetFromPLDictionary(scr->session_state, dDock);
903 scr->dock = wDockRestoreState(scr, state, WM_DOCK);
906 if (!wPreferences.flags.noclip) {
907 state = WMGetFromPLDictionary(scr->session_state, dClip);
908 scr->clip_icon = wClipRestoreState(scr, state);
911 if (!wPreferences.flags.nodrawer) {
912 if (!scr->dock->on_right_side) {
913 /* Drawer tile was created early in wScreenInit() -> wReadDefaults(). At
914 * that time, scr->dock was NULL and the tile was created as if we were on
915 * the right side. If we aren't, redo it now. */
916 assert(scr->drawer_tile);
917 RReleaseImage(scr->drawer_tile);
918 scr->drawer_tile = wDrawerMakeTile(scr, scr->icon_tile);
920 wDrawersRestoreState(scr);
923 wWorkspaceRestoreState(scr);
924 wScreenUpdateUsableArea(scr);
927 void wScreenSaveState(WScreen * scr)
929 WWindow *wwin;
930 char *str;
931 WMPropList *old_state, *foo;
933 make_keys();
935 /* save state of windows */
936 wwin = scr->focused_window;
937 while (wwin) {
938 wWindowSaveState(wwin);
939 wwin = wwin->prev;
942 if (wPreferences.flags.noupdates)
943 return;
945 old_state = scr->session_state;
946 scr->session_state = WMCreatePLDictionary(NULL, NULL);
948 WMPLSetCaseSensitive(True);
950 /* save dock state to file */
951 if (!wPreferences.flags.nodock) {
952 wDockSaveState(scr, old_state);
953 } else {
954 foo = WMGetFromPLDictionary(old_state, dDock);
955 if (foo != NULL)
956 WMPutInPLDictionary(scr->session_state, dDock, foo);
958 if (!wPreferences.flags.noclip) {
959 wClipSaveState(scr);
960 } else {
961 foo = WMGetFromPLDictionary(old_state, dClip);
962 if (foo != NULL)
963 WMPutInPLDictionary(scr->session_state, dClip, foo);
966 wWorkspaceSaveState(scr, old_state);
968 if (!wPreferences.flags.nodrawer) {
969 wDrawersSaveState(scr);
970 } else {
971 foo = WMGetFromPLDictionary(old_state, dDrawers);
972 if (foo != NULL)
973 WMPutInPLDictionary(scr->session_state, dDrawers, foo);
977 if (wPreferences.save_session_on_exit) {
978 wSessionSaveState(scr);
979 } else {
980 foo = WMGetFromPLDictionary(old_state, dApplications);
981 if (foo != NULL)
982 WMPutInPLDictionary(scr->session_state, dApplications, foo);
984 foo = WMGetFromPLDictionary(old_state, dWorkspace);
985 if (foo != NULL)
986 WMPutInPLDictionary(scr->session_state, dWorkspace, foo);
989 /* clean up */
990 WMPLSetCaseSensitive(False);
992 wMenuSaveState(scr);
994 if (w_global.screen_count == 1) {
995 str = wdefaultspathfordomain("WMState");
996 } else {
997 char buf[16];
998 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
999 str = wdefaultspathfordomain(buf);
1001 if (!WMWritePropListToFile(scr->session_state, str)) {
1002 werror(_("could not save session state in %s"), str);
1004 wfree(str);
1005 WMReleasePropList(old_state);
1008 int wScreenBringInside(WScreen * scr, int *x, int *y, int width, int height)
1010 int moved = 0;
1011 int tol_w, tol_h;
1013 * With respect to the head that contains most of the window.
1015 int sx1, sy1, sx2, sy2;
1017 WMRect rect;
1018 int head, flags;
1020 rect.pos.x = *x;
1021 rect.pos.y = *y;
1022 rect.size.width = width;
1023 rect.size.height = height;
1025 head = wGetRectPlacementInfo(scr, rect, &flags);
1026 rect = wGetRectForHead(scr, head);
1028 sx1 = rect.pos.x;
1029 sy1 = rect.pos.y;
1030 sx2 = sx1 + rect.size.width;
1031 sy2 = sy1 + rect.size.height;
1033 #if 0 /* NOTE: gives funky group movement */
1034 if (flags & XFLAG_MULTIPLE) {
1036 * since we span multiple heads, pull window totaly inside
1038 if (*x < sx1)
1039 *x = sx1, moved = 1;
1040 else if (*x + width > sx2)
1041 *x = sx2 - width, moved = 1;
1043 if (*y < sy1)
1044 *y = sy1, moved = 1;
1045 else if (*y + height > sy2)
1046 *y = sy2 - height, moved = 1;
1048 return moved;
1050 #endif
1052 if (width > 20)
1053 tol_w = width / 2;
1054 else
1055 tol_w = 20;
1057 if (height > 20)
1058 tol_h = height / 2;
1059 else
1060 tol_h = 20;
1062 if (*x + width < sx1 + 10)
1063 *x = sx1 - tol_w, moved = 1;
1064 else if (*x >= sx2 - 10)
1065 *x = sx2 - tol_w - 1, moved = 1;
1067 if (*y < sy1 - height + 10)
1068 *y = sy1 - tol_h, moved = 1;
1069 else if (*y >= sy2 - 10)
1070 *y = sy2 - tol_h - 1, moved = 1;
1072 return moved;
1075 int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height)
1077 int moved = 0;
1078 int sx1, sy1, sx2, sy2;
1079 WMRect rect;
1080 int head;
1082 rect.pos.x = *x;
1083 rect.pos.y = *y;
1084 rect.size.width = width;
1085 rect.size.height = height;
1087 head = wGetHeadForRect(scr, rect);
1088 rect = wGetRectForHead(scr, head);
1090 sx1 = rect.pos.x;
1091 sy1 = rect.pos.y;
1092 sx2 = sx1 + rect.size.width;
1093 sy2 = sy1 + rect.size.height;
1095 if (*x < sx1)
1096 *x = sx1, moved = 1;
1097 else if (*x + width > sx2)
1098 *x = sx2 - width, moved = 1;
1100 if (*y < sy1)
1101 *y = sy1, moved = 1;
1102 else if (*y + height > sy2)
1103 *y = sy2 - height, moved = 1;
1105 return moved;