Update Serbian translation from master branch
[wmaker-crm.git] / src / screen.c
blob6ed591c0d7c665272eaa862bd038afa09e199e62
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>
27 #include <time.h>
28 #include <errno.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 #ifdef KEEP_XKB_LOCK_STATUS
36 #include <X11/XKBlib.h>
37 #endif /* KEEP_XKB_LOCK_STATUS */
38 #ifdef USE_RANDR
39 #include <X11/extensions/Xrandr.h>
40 #endif
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 "window.h"
50 #include "main.h"
51 #include "actions.h"
52 #include "properties.h"
53 #include "dock.h"
54 #include "resources.h"
55 #include "workspace.h"
56 #include "session.h"
57 #include "balloon.h"
58 #include "geomview.h"
59 #include "wmspec.h"
60 #include "rootmenu.h"
61 #include "misc.h"
63 #include "xinerama.h"
65 #include <WINGs/WUtil.h>
66 #include <WINGs/WINGsP.h>
68 #include "defaults.h"
70 #define EVENT_MASK (LeaveWindowMask|EnterWindowMask|PropertyChangeMask\
71 |SubstructureNotifyMask|PointerMotionMask \
72 |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
73 |KeyPressMask|KeyReleaseMask)
75 #ifdef USE_ICCCM_WMREPLACE
76 #define REPLACE_WM_TIMEOUT 15
77 #endif
79 #define STIPPLE_WIDTH 2
80 #define STIPPLE_HEIGHT 2
81 static char STIPPLE_DATA[] = { 0x02, 0x01 };
83 static int CantManageScreen = 0;
85 static WMPropList *dApplications = NULL;
86 static WMPropList *dWorkspace;
87 static WMPropList *dDock;
88 static WMPropList *dClip;
89 static WMPropList *dDrawers = NULL;
91 static void make_keys(void)
93 if (dApplications != NULL)
94 return;
96 dApplications = WMCreatePLString("Applications");
97 dWorkspace = WMCreatePLString("Workspace");
98 dDock = WMCreatePLString("Dock");
99 dClip = WMCreatePLString("Clip");
100 dDrawers = WMCreatePLString("Drawers");
104 * Support for ICCCM 2.0: Window Manager Replacement protocol
105 * See: http://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
107 * Basically, user should be able to dynamically change its window manager; this is done
108 * cooperatively through a special Selection ("WM_Sn" where 'n' is the X screen number)
110 * This function does 2 things:
111 * - it checks if this selection is already owned, which means that another window
112 * manager is running. If it is the case and user specified '--replace' on the command
113 * line, then it asks the WM to shut down;
114 * - when ok, it sets the selection ownership to ourself, so another window manager
115 * may ask us to shut down (this is handled in "event.c")
117 static Bool replace_existing_wm(WScreen *scr)
119 #ifdef USE_ICCCM_WMREPLACE
120 char atomName[16];
121 Window wm;
122 XSetWindowAttributes attribs;
123 XClientMessageEvent event;
124 unsigned long current_time;
125 int ret;
127 /* Try to acquire the atom named WM_S<screen> */
128 ret = snprintf(atomName, sizeof(atomName), "WM_S%d", scr->screen);
129 if (ret < 0 || ret == sizeof(atomName)) {
130 werror("out of memory trying to allocate window manager selection atom for screen %d", scr->screen);
131 return False;
134 scr->sn_atom = XInternAtom(dpy, atomName, False);
135 if (! scr->sn_atom)
136 return False;
138 /* Check if an existing window manager owns the selection */
139 wm = XGetSelectionOwner(dpy, scr->sn_atom);
140 if (wm) {
141 if (!wPreferences.flags.replace) {
142 wmessage(_("another window manager is running"));
143 wwarning(_("use the --replace flag to replace it"));
144 return False;
147 attribs.event_mask = StructureNotifyMask;
148 if (!XChangeWindowAttributes(dpy, wm, CWEventMask, &attribs))
149 wm = None;
151 #endif
153 /* for our window manager info notice board and the selection owner */
154 scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, 0, 0);
156 #ifdef USE_ICCCM_WMREPLACE
157 /* Try to acquire the selection */
158 current_time = CurrentTime;
159 ret = XSetSelectionOwner(dpy, scr->sn_atom, scr->info_window, current_time);
160 if (ret == BadAtom || ret == BadWindow)
161 return False;
163 /* Wait for other window manager to exit */
164 if (wm) {
165 unsigned long wait = 0;
166 unsigned long timeout = REPLACE_WM_TIMEOUT * 1000000L;
167 XEvent event;
169 while (wait < timeout) {
170 if (!(wait % 1000000))
171 wmessage(_("waiting %lus for other window manager to exit"), (timeout - wait) / 1000000L);
173 if (XCheckWindowEvent(dpy, wm, StructureNotifyMask, &event))
174 if (event.type == DestroyNotify)
175 break;
177 wusleep(100000);
178 wait += 100000;
181 if (wait >= timeout) {
182 wwarning(_("other window manager hasn't exited!"));
183 return False;
186 wmessage(_("replacing the other window manager"));
189 if (XGetSelectionOwner(dpy, scr->sn_atom) != scr->info_window)
190 return False;
192 event.type = ClientMessage;
193 event.message_type = scr->sn_atom;
194 event.format = 32;
195 event.data.l[0] = (long) current_time;
196 event.data.l[1] = (long) scr->sn_atom;
197 event.data.l[2] = (long) scr->info_window;
198 event.data.l[3] = (long) 0L;
199 event.data.l[4] = (long) 0L;
200 event.window = scr->root_win;
201 XSendEvent(dpy, scr->root_win, False, StructureNotifyMask, (XEvent *) &event);
202 #endif
204 return True;
208 *----------------------------------------------------------------------
209 * alreadyRunningError--
210 * X error handler used to catch errors when trying to do
211 * XSelectInput() on the root window. These errors probably mean that
212 * there already is some other window manager running.
214 * Returns:
215 * Nothing, unless something really evil happens...
217 * Side effects:
218 * CantManageScreen is set to 1;
219 *----------------------------------------------------------------------
221 static int alreadyRunningError(Display * dpy, XErrorEvent * error)
223 /* Parameter not used, but tell the compiler that it is ok */
224 (void) dpy;
225 (void) error;
227 CantManageScreen = 1;
228 return -1;
232 *----------------------------------------------------------------------
233 * allocButtonPixmaps--
234 * Allocate pixmaps used on window operation buttons (those in the
235 * titlebar). The pixmaps are linked to the program. If XPM is supported
236 * XPM pixmaps are used otherwise, equivalent bitmaps are used.
238 * Returns:
239 * Nothing
241 * Side effects:
242 * Allocates shared pixmaps for the screen. These pixmaps should
243 * not be freed by anybody.
244 *----------------------------------------------------------------------
246 static void allocButtonPixmaps(WScreen * scr)
248 WPixmap *pix;
250 /* create predefined pixmaps */
251 if (wPreferences.new_style == TS_NEXT) {
252 pix = wPixmapCreateFromXPMData(scr, NEXT_CLOSE_XPM);
253 } else {
254 pix = wPixmapCreateFromXPMData(scr, PRED_CLOSE_XPM);
256 if (pix)
257 pix->shared = 1;
258 scr->b_pixmaps[WBUT_CLOSE] = pix;
260 if (wPreferences.new_style == TS_NEXT) {
261 pix = wPixmapCreateFromXPMData(scr, NEXT_BROKEN_CLOSE_XPM);
262 } else {
263 pix = wPixmapCreateFromXPMData(scr, PRED_BROKEN_CLOSE_XPM);
265 if (pix)
266 pix->shared = 1;
267 scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;
269 if (wPreferences.new_style == TS_NEXT) {
270 pix = wPixmapCreateFromXPMData(scr, NEXT_ICONIFY_XPM);
271 } else {
272 pix = wPixmapCreateFromXPMData(scr, PRED_ICONIFY_XPM);
274 if (pix)
275 pix->shared = 1;
276 scr->b_pixmaps[WBUT_ICONIFY] = pix;
277 #ifdef XKB_BUTTON_HINT
278 if (wPreferences.new_style == TS_NEXT) {
279 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP1_XPM);
280 } else {
281 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP1_XPM);
283 if (pix)
284 pix->shared = 1;
285 scr->b_pixmaps[WBUT_XKBGROUP1] = pix;
286 if (wPreferences.new_style == TS_NEXT) {
287 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP2_XPM);
288 } else {
289 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP2_XPM);
291 if (pix)
292 pix->shared = 1;
293 scr->b_pixmaps[WBUT_XKBGROUP2] = pix;
294 if (wPreferences.new_style == TS_NEXT) {
295 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP3_XPM);
296 } else {
297 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP3_XPM);
299 if (pix)
300 pix->shared = 1;
301 scr->b_pixmaps[WBUT_XKBGROUP3] = pix;
302 if (wPreferences.new_style == TS_NEXT) {
303 pix = wPixmapCreateFromXPMData(scr, NEXT_XKBGROUP4_XPM);
304 } else {
305 pix = wPixmapCreateFromXPMData(scr, PRED_XKBGROUP4_XPM);
307 if (pix)
308 pix->shared = 1;
309 scr->b_pixmaps[WBUT_XKBGROUP4] = pix;
310 #endif
312 if (wPreferences.new_style == TS_NEXT) {
313 pix = wPixmapCreateFromXPMData(scr, NEXT_KILL_XPM);
314 } else {
315 pix = wPixmapCreateFromXPMData(scr, PRED_KILL_XPM);
317 if (pix)
318 pix->shared = 1;
319 scr->b_pixmaps[WBUT_KILL] = pix;
322 static int get_dot_mult(void)
324 int dot_mult;
327 * Since it's an int, the multiplier will grow only when
328 * a certain icon size is reached (1 for 64, 2 for 128,
329 * 3 for 192, and so on).
331 dot_mult = wPreferences.icon_size / 64;
333 if (dot_mult < 1)
334 dot_mult = 1;
336 return dot_mult;
339 static void draw_dot(WScreen * scr, Drawable d, int x, int y, GC gc)
341 int dot_mult;
343 dot_mult = get_dot_mult();
344 XSetForeground(dpy, gc, scr->black_pixel);
345 XFillRectangle(dpy, d, gc, x, y, x + (2 * dot_mult), y + (1 * dot_mult));
346 XFillRectangle(dpy, d, gc, x, y + (1 * dot_mult), x + (1 * dot_mult), y + (2 * dot_mult));
347 XSetForeground(dpy, gc, scr->white_pixel);
348 XFillRectangle(dpy, d, gc, x + (2 * dot_mult), y, x + (3 * dot_mult), y + (2 * dot_mult));
349 XFillRectangle(dpy, d, gc, x + (1 * dot_mult), y + (1 * dot_mult), x + (2 * dot_mult), y + (2 * dot_mult));
352 static WPixmap *make3Dots(WScreen * scr)
354 WPixmap *wpix;
355 GC gc2, gc;
356 XGCValues gcv;
357 Pixmap pix, mask;
358 int dot_mult;
360 dot_mult = get_dot_mult();
361 gc = scr->copy_gc;
362 pix = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, scr->w_depth);
363 XSetForeground(dpy, gc, scr->black_pixel);
364 XFillRectangle(dpy, pix, gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
365 XSetForeground(dpy, gc, scr->white_pixel);
366 draw_dot(scr, pix, 4 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
367 draw_dot(scr, pix, 9 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
368 draw_dot(scr, pix, 14 * dot_mult, wPreferences.icon_size - (6 * dot_mult), gc);
370 mask = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size, wPreferences.icon_size, 1);
371 gcv.foreground = 0;
372 gcv.graphics_exposures = False;
373 gc2 = XCreateGC(dpy, mask, GCForeground | GCGraphicsExposures, &gcv);
374 XFillRectangle(dpy, mask, gc2, 0, 0, wPreferences.icon_size, wPreferences.icon_size);
375 XSetForeground(dpy, gc2, 1);
376 XFillRectangle(dpy, mask, gc2, 4 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
377 XFillRectangle(dpy, mask, gc2, 9 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
378 XFillRectangle(dpy, mask, gc2, 14 * dot_mult, wPreferences.icon_size - (6 * dot_mult), 3 * dot_mult, 2 * dot_mult);
380 XFreeGC(dpy, gc2);
382 wpix = wPixmapCreate(pix, mask);
383 wpix->shared = 1;
385 return wpix;
388 static void allocGCs(WScreen * scr)
390 XGCValues gcv;
391 XColor color;
392 int gcm;
394 scr->stipple_bitmap = XCreateBitmapFromData(dpy, scr->w_win, STIPPLE_DATA, STIPPLE_WIDTH, STIPPLE_HEIGHT);
396 gcv.stipple = scr->stipple_bitmap;
397 gcv.foreground = scr->white_pixel;
398 gcv.fill_style = FillStippled;
399 gcv.graphics_exposures = False;
400 gcm = GCForeground | GCStipple | GCFillStyle | GCGraphicsExposures;
401 scr->stipple_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
403 /* selected icon border GCs */
404 gcv.function = GXcopy;
405 gcv.foreground = scr->white_pixel;
406 gcv.background = scr->black_pixel;
407 gcv.line_width = 1;
408 gcv.line_style = LineDoubleDash;
409 gcv.fill_style = FillSolid;
410 gcv.dash_offset = 0;
411 gcv.dashes = 4;
412 gcv.graphics_exposures = False;
414 gcm = GCFunction | GCGraphicsExposures;
415 gcm |= GCForeground | GCBackground;
416 gcm |= GCLineWidth | GCLineStyle;
417 gcm |= GCFillStyle;
418 gcm |= GCDashOffset | GCDashList;
420 scr->icon_select_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
422 scr->menu_title_color[0] = WMRetainColor(scr->white);
424 /* don't retain scr->black here because we may alter its alpha */
425 scr->mtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
426 scr->dtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True);
428 /* frame GC */
429 wGetColor(scr, DEF_FRAME_COLOR, &color);
430 gcv.function = GXxor;
431 /* this will raise the probability of the XORed color being different
432 * of the original color in PseudoColor when not all color cells are
433 * initialized */
434 if (DefaultVisual(dpy, scr->screen)->class == PseudoColor)
435 gcv.plane_mask = (1 << (scr->depth - 1)) | 1;
436 else
437 gcv.plane_mask = AllPlanes;
438 gcv.foreground = color.pixel;
439 if (gcv.foreground == 0)
440 gcv.foreground = 1;
441 gcv.line_width = DEF_FRAME_THICKNESS;
442 gcv.subwindow_mode = IncludeInferiors;
443 gcv.graphics_exposures = False;
444 scr->frame_gc = XCreateGC(dpy, scr->root_win, GCForeground | GCGraphicsExposures
445 | GCFunction | GCSubwindowMode | GCLineWidth | GCPlaneMask, &gcv);
447 /* line GC */
448 gcv.foreground = color.pixel;
450 if (gcv.foreground == 0)
451 /* XOR:ing with a zero is not going to be of much use, so
452 in that case, we somewhat arbitrarily xor with 17 instead. */
453 gcv.foreground = 17;
455 gcv.function = GXxor;
456 gcv.subwindow_mode = IncludeInferiors;
457 gcv.line_width = 1;
458 gcv.cap_style = CapRound;
459 gcv.graphics_exposures = False;
460 gcm = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth | GCCapStyle | GCGraphicsExposures;
461 scr->line_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);
463 scr->line_pixel = gcv.foreground;
465 /* copy GC */
466 gcv.foreground = scr->white_pixel;
467 gcv.background = scr->black_pixel;
468 gcv.graphics_exposures = False;
469 scr->copy_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
471 /* misc drawing GC */
472 gcv.graphics_exposures = False;
473 gcm = GCGraphicsExposures;
474 scr->draw_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
476 assert(scr->stipple_bitmap != None);
478 /* mono GC */
479 scr->mono_gc = XCreateGC(dpy, scr->stipple_bitmap, gcm, &gcv);
482 static void createPixmaps(WScreen * scr)
484 WPixmap *pix;
486 /* load pixmaps */
487 #define LOADPIXMAPINDICATOR(XBM,W,I) {\
488 pix = wPixmapCreateFromXBMData(scr, (char *)XBM, (char *)XBM,\
489 W, MENU_SNAP_INDICATOR_H_XBM_SIZE,\
490 scr->black_pixel, scr->white_pixel);\
491 if (pix != NULL)\
492 pix->shared = 1;\
493 scr->I = pix;}
495 LOADPIXMAPINDICATOR(MENU_RADIO_INDICATOR_XBM_DATA, MENU_RADIO_INDICATOR_XBM_SIZE, menu_radio_indicator)
496 LOADPIXMAPINDICATOR(MENU_CHECK_INDICATOR_XBM_DATA, MENU_CHECK_INDICATOR_XBM_SIZE, menu_check_indicator)
497 LOADPIXMAPINDICATOR(MENU_MINI_INDICATOR_XBM_DATA, MENU_MINI_INDICATOR_XBM_SIZE, menu_mini_indicator)
498 LOADPIXMAPINDICATOR(MENU_HIDE_INDICATOR_XBM_DATA, MENU_HIDE_INDICATOR_XBM_SIZE, menu_hide_indicator)
499 LOADPIXMAPINDICATOR(MENU_SHADE_INDICATOR_XBM_DATA, MENU_SHADE_INDICATOR_XBM_SIZE, menu_shade_indicator)
501 LOADPIXMAPINDICATOR(MENU_SNAP_V_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_vertical_indicator)
502 LOADPIXMAPINDICATOR(MENU_SNAP_H_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_horizontal_indicator)
503 LOADPIXMAPINDICATOR(MENU_CENTRAL_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_central_indicator)
504 LOADPIXMAPINDICATOR(MENU_SNAP_RH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_rh_indicator)
505 LOADPIXMAPINDICATOR(MENU_SNAP_LH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_lh_indicator)
506 LOADPIXMAPINDICATOR(MENU_SNAP_TH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_th_indicator)
507 LOADPIXMAPINDICATOR(MENU_SNAP_BH_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_bh_indicator)
508 LOADPIXMAPINDICATOR(MENU_SNAP_TL_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_tl_indicator)
509 LOADPIXMAPINDICATOR(MENU_SNAP_TR_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_tr_indicator)
510 LOADPIXMAPINDICATOR(MENU_SNAP_BL_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_bl_indicator)
511 LOADPIXMAPINDICATOR(MENU_SNAP_BR_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_br_indicator)
512 LOADPIXMAPINDICATOR(MENU_SNAP_TILED_INDICATOR_XBM_DATA, MENU_SNAP_INDICATOR_W_XBM_SIZE, menu_snap_tiled_indicator)
514 #undef LOADPIXMAPINDICATOR
516 create_logo_image(scr);
518 scr->dock_dots = make3Dots(scr);
520 /* titlebar button pixmaps */
521 allocButtonPixmaps(scr);
524 void create_logo_image(WScreen *scr)
526 RImage *image = get_icon_image(scr, "Logo", "WMPanel", 128);
528 if (!image) {
529 wwarning(_("could not load logo image for panels: %s"), RMessageForError(RErrorCode));
530 } else {
531 WMSetApplicationIconImage(scr->wmscreen, image);
532 RReleaseImage(image);
537 *----------------------------------------------------------------------
538 * createInternalWindows--
539 * Creates some windows used internally by the program. One to
540 * receive input focus when no other window can get it and another
541 * to display window geometry information during window resize/move.
543 * Returns:
544 * Nothing
546 * Side effects:
547 * Windows are created and some colors are allocated for the
548 * window background.
549 *----------------------------------------------------------------------
551 static void createInternalWindows(WScreen * scr)
553 int vmask;
554 XSetWindowAttributes attribs;
556 /* InputOnly window to get the focus when no other window can get it */
557 vmask = CWEventMask | CWOverrideRedirect;
558 attribs.event_mask = KeyPressMask | FocusChangeMask;
559 attribs.override_redirect = True;
560 scr->no_focus_win = XCreateWindow(dpy, scr->root_win, -10, -10, 4, 4, 0, 0,
561 InputOnly, CopyFromParent, vmask, &attribs);
562 XSelectInput(dpy, scr->no_focus_win, KeyPressMask | KeyReleaseMask);
563 XMapWindow(dpy, scr->no_focus_win);
565 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
567 /* shadow window for dock buttons */
568 vmask = CWBorderPixel | CWBackPixmap | CWBackPixel | CWCursor | CWSaveUnder | CWOverrideRedirect;
569 attribs.border_pixel = scr->black_pixel;
570 attribs.save_under = True;
571 attribs.override_redirect = True;
572 attribs.background_pixmap = None;
573 attribs.background_pixel = scr->white_pixel;
574 attribs.cursor = wPreferences.cursor[WCUR_NORMAL];
575 vmask |= CWColormap;
576 attribs.colormap = scr->w_colormap;
577 scr->dock_shadow =
578 XCreateWindow(dpy, scr->root_win, 0, 0, wPreferences.icon_size,
579 wPreferences.icon_size, 0, scr->w_depth, CopyFromParent, scr->w_visual, vmask, &attribs);
581 /* workspace name */
582 vmask = CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWColormap | CWBorderPixel;
583 attribs.save_under = True;
584 attribs.override_redirect = True;
585 attribs.colormap = scr->w_colormap;
586 attribs.background_pixel = scr->icon_back_texture->normal.pixel;
587 attribs.border_pixel = 0; /* do not care */
588 scr->workspace_name =
589 XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
590 CopyFromParent, scr->w_visual, vmask, &attribs);
591 scr->mini_screenshot_timeout = 0;
595 *----------------------------------------------------------------------
596 * wScreenInit--
597 * Initializes the window manager for the given screen and
598 * allocates a WScreen descriptor for it. Many resources are allocated
599 * for the screen and the root window is setup appropriately.
601 * Returns:
602 * The WScreen descriptor for the screen.
604 * Side effects:
605 * Many resources are allocated and the IconSize property is
606 * set on the root window.
607 * The program can be aborted if some fatal error occurs.
609 * TODO: User specifiable visual.
610 *----------------------------------------------------------------------
612 WScreen *wScreenInit(int screen_number)
614 WScreen *scr;
615 XIconSize icon_size[1];
616 RContextAttributes rattr = {};
617 long event_mask;
618 XErrorHandler oldHandler;
619 int i;
621 scr = wmalloc(sizeof(WScreen));
623 scr->stacking_list = WMCreateTreeBag();
625 /* initialize globals */
626 scr->screen = screen_number;
627 scr->root_win = RootWindow(dpy, screen_number);
628 scr->depth = DefaultDepth(dpy, screen_number);
629 scr->colormap = DefaultColormap(dpy, screen_number);
631 scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
632 scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));
634 wInitXinerama(scr);
636 scr->usableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
637 scr->totalUsableArea = (WArea *) wmalloc(sizeof(WArea) * wXineramaHeads(scr));
639 for (i = 0; i < wXineramaHeads(scr); ++i) {
640 WMRect rect = wGetRectForHead(scr, i);
641 scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = rect.pos.x;
642 scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = rect.pos.y;
643 scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
644 scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
647 scr->fakeGroupLeaders = WMCreateArray(16);
649 CantManageScreen = 0;
650 oldHandler = XSetErrorHandler(alreadyRunningError);
652 /* Do we need to replace an existing window manager? */
653 if (!replace_existing_wm(scr)) {
654 XDestroyWindow(dpy, scr->info_window);
655 wfree(scr);
656 return NULL;
659 event_mask = EVENT_MASK;
660 XSelectInput(dpy, scr->root_win, event_mask);
662 #ifdef KEEP_XKB_LOCK_STATUS
663 /* Only GroupLock doesn't work correctly in my system since right-alt
664 * can change mode while holding it too - ]d
666 if (w_global.xext.xkb.supported) {
667 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask);
669 #endif /* KEEP_XKB_LOCK_STATUS */
671 #ifdef USE_RANDR
672 if (w_global.xext.randr.supported)
673 XRRSelectInput(dpy, scr->root_win, RRScreenChangeNotifyMask);
674 #endif
676 XSync(dpy, False);
677 XSetErrorHandler(oldHandler);
679 if (CantManageScreen) {
680 wfree(scr);
681 return NULL;
684 XDefineCursor(dpy, scr->root_win, wPreferences.cursor[WCUR_ROOT]);
686 /* screen descriptor for raster graphic library */
687 rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap;
688 rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering;
690 /* if the std colormap stuff works ok, this will be ignored */
691 rattr.colors_per_channel = wPreferences.cmap_size;
692 if (rattr.colors_per_channel < 2)
693 rattr.colors_per_channel = 2;
695 /* Use standard colormap */
696 rattr.standard_colormap_mode = RUseStdColormap;
698 if (getWVisualID(screen_number) >= 0) {
699 rattr.flags |= RC_VisualID;
700 rattr.visualid = getWVisualID(screen_number);
703 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
705 if (!scr->rcontext && RErrorCode == RERR_STDCMAPFAIL) {
706 wwarning("%s", RMessageForError(RErrorCode));
708 rattr.flags &= ~RC_StandardColormap;
709 rattr.standard_colormap_mode = RUseStdColormap;
711 scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
713 if (scr->rcontext == NULL) {
714 wfatal(_("can't create Context on screen %d, %s"),
715 screen_number, RMessageForError(RErrorCode));
716 goto abort_no_context;
719 scr->w_win = scr->rcontext->drawable;
720 scr->w_visual = scr->rcontext->visual;
721 scr->w_depth = scr->rcontext->depth;
722 scr->w_colormap = scr->rcontext->cmap;
724 /* create screen descriptor for WINGs */
725 scr->wmscreen = WMCreateScreenWithRContext(dpy, screen_number, scr->rcontext);
727 if (!scr->wmscreen) {
728 wfatal(_("could not initialize WINGs widget set"));
729 RDestroyContext(scr->rcontext);
730 abort_no_context:
731 WMFreeArray(scr->fakeGroupLeaders);
732 wfree(scr->totalUsableArea);
733 wfree(scr->usableArea);
734 WMFreeBag(scr->stacking_list);
735 wfree(scr);
736 return NULL;
739 scr->black = WMBlackColor(scr->wmscreen);
740 scr->white = WMWhiteColor(scr->wmscreen);
741 scr->gray = WMGrayColor(scr->wmscreen);
742 scr->darkGray = WMDarkGrayColor(scr->wmscreen);
744 scr->black_pixel = WMColorPixel(scr->black); /*scr->rcontext->black; */
745 scr->white_pixel = WMColorPixel(scr->white); /*scr->rcontext->white; */
746 scr->light_pixel = WMColorPixel(scr->gray);
747 scr->dark_pixel = WMColorPixel(scr->darkGray);
749 /* create GCs with default values */
750 allocGCs(scr);
752 /* read defaults for this screen */
753 wReadDefaults(scr, w_global.domain.wmaker->dictionary);
756 XColor xcol;
757 /* frame boder color */
758 wGetColor(scr, WMGetColorRGBDescription(scr->frame_border_color), &xcol);
759 scr->frame_border_pixel = xcol.pixel;
760 wGetColor(scr, WMGetColorRGBDescription(scr->frame_focused_border_color), &xcol);
761 scr->frame_focused_border_pixel = xcol.pixel;
762 wGetColor(scr, WMGetColorRGBDescription(scr->frame_selected_border_color), &xcol);
763 scr->frame_selected_border_pixel = xcol.pixel;
766 createInternalWindows(scr);
768 wNETWMInitStuff(scr);
770 /* create initial workspace */
771 wWorkspaceNew(scr);
773 /* create shared pixmaps */
774 createPixmaps(scr);
776 /* set icon sizes we can accept from clients */
777 icon_size[0].min_width = 8;
778 icon_size[0].min_height = 8;
779 icon_size[0].max_width = wPreferences.icon_size - 4;
780 icon_size[0].max_height = wPreferences.icon_size - 4;
781 icon_size[0].width_inc = 1;
782 icon_size[0].height_inc = 1;
783 XSetIconSizes(dpy, scr->root_win, icon_size, 1);
785 /* setup WindowMaker protocols property in the root window */
786 PropSetWMakerProtocols(scr->root_win);
788 /* setup our noticeboard */
789 XChangeProperty(dpy, scr->info_window, w_global.atom.wmaker.noticeboard,
790 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
791 XChangeProperty(dpy, scr->root_win, w_global.atom.wmaker.noticeboard,
792 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
794 #ifdef BALLOON_TEXT
795 /* initialize balloon text stuff */
796 wBalloonInitialize(scr);
797 #endif
799 scr->info_text_font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
801 scr->tech_draw_font = XLoadQueryFont(dpy, "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*");
802 if (!scr->tech_draw_font)
803 scr->tech_draw_font = XLoadQueryFont(dpy, "fixed");
805 scr->gview = WCreateGeometryView(scr->wmscreen);
806 WMRealizeWidget(scr->gview);
808 wScreenUpdateUsableArea(scr);
810 return scr;
813 void wScreenUpdateUsableArea(WScreen *scr)
816 * scr->totalUsableArea[] will become the usableArea used for Windowplacement,
817 * scr->usableArea[] will be used for iconplacement, hence no iconyard nor
818 * border.
821 WArea area;
822 int i;
823 unsigned long tmp_area, best_area = 0;
824 unsigned int size = wPreferences.workspace_border_size;
825 unsigned int position = wPreferences.workspace_border_position;
827 for (i = 0; i < wXineramaHeads(scr); ++i) {
828 WMRect rect = wGetRectForHead(scr, i);
829 scr->totalUsableArea[i].x1 = rect.pos.x;
830 scr->totalUsableArea[i].y1 = rect.pos.y;
831 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
832 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
834 if (wNETWMGetUsableArea(scr, i, &area)) {
835 scr->totalUsableArea[i].x1 = WMAX(scr->totalUsableArea[i].x1, area.x1);
836 scr->totalUsableArea[i].y1 = WMAX(scr->totalUsableArea[i].y1, area.y1);
837 scr->totalUsableArea[i].x2 = WMIN(scr->totalUsableArea[i].x2, area.x2);
838 scr->totalUsableArea[i].y2 = WMIN(scr->totalUsableArea[i].y2, area.y2);
841 scr->usableArea[i] = scr->totalUsableArea[i];
843 if (wPreferences.no_window_over_icons) {
844 if (wPreferences.icon_yard & IY_VERT) {
845 if (wPreferences.icon_yard & IY_RIGHT)
846 scr->totalUsableArea[i].x2 -= wPreferences.icon_size;
847 else
848 scr->totalUsableArea[i].x1 += wPreferences.icon_size;
849 } else {
850 if (wPreferences.icon_yard & IY_TOP)
851 scr->totalUsableArea[i].y1 += wPreferences.icon_size;
852 else
853 scr->totalUsableArea[i].y2 -= wPreferences.icon_size;
857 if (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1 < rect.size.width / 2) {
858 scr->totalUsableArea[i].x1 = rect.pos.x;
859 scr->totalUsableArea[i].x2 = rect.pos.x + rect.size.width;
862 if (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1 < rect.size.height / 2) {
863 scr->totalUsableArea[i].y1 = rect.pos.y;
864 scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
867 tmp_area = (scr->totalUsableArea[i].x2 - scr->totalUsableArea[i].x1) *
868 (scr->totalUsableArea[i].y2 - scr->totalUsableArea[i].y1);
870 if (tmp_area > best_area) {
871 best_area = tmp_area;
872 area = scr->totalUsableArea[i];
875 if (size > 0 && position != WB_NONE) {
876 if (position & WB_LEFTRIGHT) {
877 scr->totalUsableArea[i].x1 += size;
878 scr->totalUsableArea[i].x2 -= size;
880 if (position & WB_TOPBOTTOM) {
881 scr->totalUsableArea[i].y1 += size;
882 scr->totalUsableArea[i].y2 -= size;
887 if (wPreferences.auto_arrange_icons)
888 wArrangeIcons(scr, True);
891 void wScreenRestoreState(WScreen * scr)
893 WMPropList *state;
894 char *path;
896 OpenRootMenu(scr, -10000, -10000, False);
897 wMenuUnmap(scr->root_menu);
899 make_keys();
901 if (w_global.screen_count == 1) {
902 path = wdefaultspathfordomain("WMState");
903 } else {
904 char buf[16];
905 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
906 path = wdefaultspathfordomain(buf);
908 scr->session_state = WMReadPropListFromFile(path);
909 wfree(path);
910 if (!scr->session_state && w_global.screen_count > 1) {
911 path = wdefaultspathfordomain("WMState");
912 scr->session_state = WMReadPropListFromFile(path);
913 wfree(path);
916 if (!scr->session_state)
917 scr->session_state = WMCreatePLDictionary(NULL, NULL);
919 if (!wPreferences.flags.nodock) {
920 state = WMGetFromPLDictionary(scr->session_state, dDock);
921 scr->dock = wDockRestoreState(scr, state, WM_DOCK);
924 if (!wPreferences.flags.noclip) {
925 state = WMGetFromPLDictionary(scr->session_state, dClip);
926 scr->clip_icon = wClipRestoreState(scr, state);
929 if (!wPreferences.flags.nodrawer) {
930 if (!scr->dock->on_right_side) {
931 /* Drawer tile was created early in wScreenInit() -> wReadDefaults(). At
932 * that time, scr->dock was NULL and the tile was created as if we were on
933 * the right side. If we aren't, redo it now. */
934 assert(scr->drawer_tile);
935 RReleaseImage(scr->drawer_tile);
936 scr->drawer_tile = wDrawerMakeTile(scr, scr->icon_tile);
938 wDrawersRestoreState(scr);
941 wWorkspaceRestoreState(scr);
942 wScreenUpdateUsableArea(scr);
945 void wScreenSaveState(WScreen * scr)
947 WWindow *wwin;
948 char *str;
949 WMPropList *old_state, *foo;
951 make_keys();
953 /* save state of windows */
954 wwin = scr->focused_window;
955 while (wwin) {
956 wWindowSaveState(wwin);
957 wwin = wwin->prev;
960 if (wPreferences.flags.noupdates)
961 return;
963 old_state = scr->session_state;
964 scr->session_state = WMCreatePLDictionary(NULL, NULL);
966 WMPLSetCaseSensitive(True);
968 /* save dock state to file */
969 if (!wPreferences.flags.nodock) {
970 wDockSaveState(scr, old_state);
971 } else {
972 foo = WMGetFromPLDictionary(old_state, dDock);
973 if (foo != NULL)
974 WMPutInPLDictionary(scr->session_state, dDock, foo);
976 if (!wPreferences.flags.noclip) {
977 wClipSaveState(scr);
978 } else {
979 foo = WMGetFromPLDictionary(old_state, dClip);
980 if (foo != NULL)
981 WMPutInPLDictionary(scr->session_state, dClip, foo);
984 wWorkspaceSaveState(scr, old_state);
986 if (!wPreferences.flags.nodrawer) {
987 wDrawersSaveState(scr);
988 } else {
989 foo = WMGetFromPLDictionary(old_state, dDrawers);
990 if (foo != NULL)
991 WMPutInPLDictionary(scr->session_state, dDrawers, foo);
995 if (wPreferences.save_session_on_exit) {
996 wSessionSaveState(scr);
997 } else {
998 foo = WMGetFromPLDictionary(old_state, dApplications);
999 if (foo != NULL)
1000 WMPutInPLDictionary(scr->session_state, dApplications, foo);
1002 foo = WMGetFromPLDictionary(old_state, dWorkspace);
1003 if (foo != NULL)
1004 WMPutInPLDictionary(scr->session_state, dWorkspace, foo);
1007 /* clean up */
1008 WMPLSetCaseSensitive(False);
1010 wMenuSaveState(scr);
1012 if (w_global.screen_count == 1) {
1013 str = wdefaultspathfordomain("WMState");
1014 } else {
1015 char buf[16];
1016 snprintf(buf, sizeof(buf), "WMState.%i", scr->screen);
1017 str = wdefaultspathfordomain(buf);
1019 if (!WMWritePropListToFile(scr->session_state, str)) {
1020 werror(_("could not save session state in %s"), str);
1022 wfree(str);
1023 WMReleasePropList(old_state);
1026 int wScreenBringInside(WScreen * scr, int *x, int *y, int width, int height)
1028 int moved = 0;
1029 int tol_w, tol_h;
1031 * With respect to the head that contains most of the window.
1033 int sx1, sy1, sx2, sy2;
1035 WMRect rect;
1036 int head, flags;
1038 rect.pos.x = *x;
1039 rect.pos.y = *y;
1040 rect.size.width = width;
1041 rect.size.height = height;
1043 head = wGetRectPlacementInfo(scr, rect, &flags);
1044 rect = wGetRectForHead(scr, head);
1046 sx1 = rect.pos.x;
1047 sy1 = rect.pos.y;
1048 sx2 = sx1 + rect.size.width;
1049 sy2 = sy1 + rect.size.height;
1051 #if 0 /* NOTE: gives funky group movement */
1052 if (flags & XFLAG_MULTIPLE) {
1054 * since we span multiple heads, pull window totaly inside
1056 if (*x < sx1)
1057 *x = sx1, moved = 1;
1058 else if (*x + width > sx2)
1059 *x = sx2 - width, moved = 1;
1061 if (*y < sy1)
1062 *y = sy1, moved = 1;
1063 else if (*y + height > sy2)
1064 *y = sy2 - height, moved = 1;
1066 return moved;
1068 #endif
1070 if (width > 20)
1071 tol_w = width / 2;
1072 else
1073 tol_w = 20;
1075 if (height > 20)
1076 tol_h = height / 2;
1077 else
1078 tol_h = 20;
1080 if (*x + width < sx1 + 10)
1081 *x = sx1 - tol_w, moved = 1;
1082 else if (*x >= sx2 - 10)
1083 *x = sx2 - tol_w - 1, moved = 1;
1085 if (*y < sy1 - height + 10)
1086 *y = sy1 - tol_h, moved = 1;
1087 else if (*y >= sy2 - 10)
1088 *y = sy2 - tol_h - 1, moved = 1;
1090 return moved;
1093 int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height)
1095 int moved = 0;
1096 int sx1, sy1, sx2, sy2;
1097 WMRect rect;
1098 int head;
1100 rect.pos.x = *x;
1101 rect.pos.y = *y;
1102 rect.size.width = width;
1103 rect.size.height = height;
1105 head = wGetHeadForRect(scr, rect);
1106 rect = wGetRectForHead(scr, head);
1108 sx1 = rect.pos.x;
1109 sy1 = rect.pos.y;
1110 sx2 = sx1 + rect.size.width;
1111 sy2 = sy1 + rect.size.height;
1113 if (*x < sx1)
1114 *x = sx1, moved = 1;
1115 else if (*x + width > sx2)
1116 *x = sx2 - width, moved = 1;
1118 if (*y < sy1)
1119 *y = sy1, moved = 1;
1120 else if (*y + height > sy2)
1121 *y = sy2 - height, moved = 1;
1123 return moved;
1126 static XImage *imageCaptureArea(WScreen *scr)
1128 XEvent event;
1129 int quit = 0;
1130 int xp = -1;
1131 int yp = -1;
1132 int w = 0, h = 0;
1133 int x = xp, y = yp;
1135 if (XGrabPointer(dpy, scr->root_win, False, ButtonMotionMask
1136 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
1137 GrabModeAsync, None, wPreferences.cursor[WCUR_CAPTURE], CurrentTime) != Success) {
1138 return NULL;
1141 XGrabServer(dpy);
1143 while (!quit) {
1144 WMMaskEvent(dpy, ButtonReleaseMask | PointerMotionMask | ButtonPressMask | KeyPressMask, &event);
1146 switch (event.type) {
1147 case ButtonPress:
1148 if (event.xbutton.button == Button1) {
1149 xp = event.xbutton.x_root;
1150 yp = event.xbutton.y_root;
1152 break;
1153 case ButtonRelease:
1154 if (event.xbutton.button == Button1) {
1155 quit = 1;
1156 if (w > 0 && h > 0) {
1157 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
1158 XUngrabServer(dpy);
1159 XUngrabPointer(dpy, CurrentTime);
1160 return XGetImage(dpy, scr->root_win, x, y, w, h, AllPlanes, ZPixmap);
1163 break;
1164 case MotionNotify:
1165 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
1166 x = event.xmotion.x_root;
1167 if (x < xp) {
1168 w = xp - x;
1169 } else {
1170 w = x - xp;
1171 x = xp;
1173 y = event.xmotion.y_root;
1174 if (y < yp) {
1175 h = yp - y;
1176 } else {
1177 h = y - yp;
1178 y = yp;
1180 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
1181 break;
1182 case KeyPress:
1183 if (W_KeycodeToKeysym(dpy, event.xkey.keycode, 0) == XK_Escape)
1184 quit = 1;
1185 break;
1186 default:
1187 WMHandleEvent(&event);
1188 break;
1192 XUngrabServer(dpy);
1193 XUngrabPointer(dpy, CurrentTime);
1194 return NULL;
1197 static void hideMiniScreenshot(void *data)
1199 WScreen *scr = (WScreen *) data;
1201 if (time(NULL) < scr->mini_screenshot_timeout) {
1202 scr->mini_screenshot_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideMiniScreenshot, scr);
1203 } else {
1204 XWindowAttributes attr;
1206 WMDeleteTimerHandler(scr->mini_screenshot_timer);
1207 if (XGetWindowAttributes(dpy, scr->mini_screenshot, &attr))
1208 slide_window(scr->mini_screenshot, attr.x, attr.y, attr.x + attr.width, attr.y);
1209 XUnmapWindow(dpy, scr->mini_screenshot);
1210 XDestroyWindow(dpy, scr->mini_screenshot);
1211 scr->mini_screenshot_timeout = 0;
1215 static void showMiniScreenshot(WScreen *scr, RImage *img)
1217 Pixmap pix;
1218 int x = scr->scr_width - img->width - 20;
1219 int y = scr->scr_height - img->height - 20;
1221 if (!scr->mini_screenshot_timeout) {
1222 Window win;
1224 win = XCreateSimpleWindow(dpy, scr->root_win, x, y,
1225 img->width, img->height, scr->frame_border_width, 0, 0);
1226 scr->mini_screenshot = win;
1228 RConvertImage(scr->rcontext, img, &pix);
1229 XMapWindow(dpy, scr->mini_screenshot);
1230 XCopyArea(dpy, pix, scr->mini_screenshot, scr->rcontext->copy_gc, 0, 0, img->width, img->height, 0, 0);
1231 XFlush(dpy);
1233 scr->mini_screenshot_timeout = time(NULL) + 2;
1234 scr->mini_screenshot_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideMiniScreenshot, scr);
1237 void ScreenCapture(WScreen *scr, int mode)
1239 time_t s;
1240 short i = 0;
1241 struct tm *tm_info;
1242 char index_str[12] = "";
1243 char filename_date_part[60];
1244 char filename[60];
1245 char *filepath;
1246 char *screenshot_dir;
1247 RImage *img = NULL;
1248 RImage *scale_img = NULL;
1250 #ifdef USE_PNG
1251 char *filetype = ".png";
1252 #else
1253 #ifdef USE_JPEG
1254 char *filetype = ".jpg";
1255 #else
1256 char *filetype = NULL;
1257 #endif
1258 #endif
1260 if (!filetype) {
1261 werror(_("Unable to find a proper screenshot image format"));
1262 return;
1265 screenshot_dir = wstrconcat(wusergnusteppath(), "/Library/WindowMaker/Screenshots/");
1266 if (-1 == mkdir(screenshot_dir, 0700) && errno != EEXIST) {
1267 wfree(screenshot_dir);
1268 werror(_("Unable to create screenshot directory: %s"), strerror(errno));
1269 return;
1272 s = time(NULL);
1273 tm_info = localtime(&s);
1274 strftime(filename_date_part, sizeof(filename_date_part), "screenshot_%Y-%m-%d_at_%H:%M:%S", tm_info);
1275 strcpy(filename, filename_date_part);
1277 filepath = wstrconcat(screenshot_dir, strcat(filename, filetype));
1278 while (access(filepath, F_OK) == 0 && i < 600) {
1279 i++;
1280 strcpy(filename, filename_date_part);
1281 sprintf(index_str, "_%d", i);
1282 strncat(filename, index_str, sizeof(filename) - strlen(filename) - 1);
1283 wfree(filepath);
1284 filepath = wstrconcat(screenshot_dir, strcat(filename, filetype));
1287 /* cannot generate an available filename ?! */
1288 if (i == 600) {
1289 wfree(filepath);
1290 wfree(screenshot_dir);
1291 werror(_("Could not generate a free screenshot filename"));
1292 return;
1295 switch (mode) {
1296 WWindow *wwin;
1297 XImage *pimg;
1299 case PRINT_WINDOW:
1300 wwin = scr->focused_window;
1301 if (wwin && !wwin->flags.shaded) {
1303 * check if hint WM_TAKE_FOCUS is set, if it's the case
1304 * we can take screenshot of the out of screen window
1306 if (wwin->focus_mode >= WFM_LOCALLY_ACTIVE) {
1307 img = RCreateImageFromDrawable(scr->rcontext, wwin->client_win, None);
1309 else {
1310 /* we will only capture the visible window part */
1311 int x_crop = 0;
1312 int y_crop = 0;
1313 int w_crop = wwin->client.width;
1314 int h_crop = wwin->client.height;
1316 if (wwin->client.x > 0)
1317 x_crop = wwin->client.x;
1318 if (wwin->client.y > 0)
1319 y_crop = wwin->client.y;
1321 if (wwin->client.x + wwin->client.width > scr->scr_width)
1322 w_crop = scr->scr_width - wwin->client.x;
1323 if (wwin->client.y + wwin->client.height > scr->scr_height)
1324 h_crop = scr->scr_height - wwin->client.y;
1326 pimg = XGetImage(dpy, scr->root_win, x_crop, y_crop,
1327 (wwin->client.x > 0)?w_crop:w_crop + wwin->client.x,
1328 (wwin->client.y > 0)?h_crop:h_crop + wwin->client.y,
1329 AllPlanes, ZPixmap);
1331 if (pimg) {
1332 img = RCreateImageFromXImage(scr->rcontext, pimg, None);
1333 XDestroyImage(pimg);
1337 break;
1338 case PRINT_PARTIAL:
1339 pimg = imageCaptureArea(scr);
1340 if (pimg) {
1341 img = RCreateImageFromXImage(scr->rcontext, pimg, None);
1342 XDestroyImage(pimg);
1344 break;
1345 default:
1346 /* PRINT_SCREEN*/
1347 img = RCreateImageFromDrawable(scr->rcontext, scr->root_win, None);
1350 if (img) {
1351 #ifdef USE_PNG
1352 if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
1353 #else
1354 if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
1355 #endif
1356 scale_img = RSmoothScaleImage(img, scr->scr_width / 10, scr->scr_height / 10);
1357 showMiniScreenshot(scr, scale_img);
1358 RReleaseImage(scale_img);
1359 #ifdef DEBUG
1360 wmessage("screenshot filepath: %s", filepath);
1361 #endif
1363 RReleaseImage(img);
1365 wfree(filepath);
1366 wfree(screenshot_dir);