some documentation updates
[wmaker-crm.git] / src / workspace.c
blob2ff87afd5309e41ebec452a50875cf0418869408
1 /* workspace.c- Workspace 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
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.
22 #include "wconfig.h"
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 #ifdef SHAPE
27 #include <X11/extensions/shape.h>
28 #endif
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <time.h>
36 #include <sys/time.h>
38 #include "WindowMaker.h"
39 #include "wcore.h"
40 #include "framewin.h"
41 #include "window.h"
42 #include "icon.h"
43 #include "funcs.h"
44 #include "menu.h"
45 #include "application.h"
46 #include "dock.h"
47 #include "actions.h"
48 #include "workspace.h"
49 #include "appicon.h"
50 #ifdef NETWM_HINTS
51 #include "wmspec.h"
52 #endif
54 #include "xinerama.h"
57 extern WPreferences wPreferences;
58 extern XContext wWinContext;
59 extern XContext wVEdgeContext;
61 extern void ProcessPendingEvents();
63 static WMPropList *dWorkspaces=NULL;
64 static WMPropList *dClip, *dName;
66 #ifdef VIRTUAL_DESKTOP
67 static BOOL initVDesk = False;
68 #endif
71 static void
72 make_keys()
74 if (dWorkspaces!=NULL)
75 return;
77 dWorkspaces = WMCreatePLString("Workspaces");
78 dName = WMCreatePLString("Name");
79 dClip = WMCreatePLString("Clip");
83 void
84 wWorkspaceMake(WScreen *scr, int count)
86 while (count>0) {
87 wWorkspaceNew(scr);
88 count--;
93 int
94 wWorkspaceNew(WScreen *scr)
96 WWorkspace *wspace, **list;
97 int i;
99 if (scr->workspace_count < MAX_WORKSPACES) {
100 scr->workspace_count++;
102 wspace = wmalloc(sizeof(WWorkspace));
103 wspace->name = NULL;
105 if (!wspace->name) {
106 wspace->name = wmalloc(strlen(_("Workspace %i"))+8);
107 sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
111 if (!wPreferences.flags.noclip) {
112 wspace->clip = wDockCreate(scr, WM_CLIP);
113 } else
114 wspace->clip = NULL;
116 list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
118 for (i=0; i<scr->workspace_count-1; i++) {
119 list[i] = scr->workspaces[i];
121 list[i] = wspace;
122 if (scr->workspaces)
123 wfree(scr->workspaces);
124 scr->workspaces = list;
126 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
127 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
128 #ifdef VIRTUAL_DESKTOP
129 wspace->view_x = wspace->view_y = 0;
130 wspace->height = scr->scr_height;
131 wspace->width = scr->scr_width;
132 #endif
133 #ifdef NETWM_HINTS
134 wNETWMUpdateDesktop(scr);
135 #endif
137 WMPostNotificationName(WMNWorkspaceCreated, scr,
138 (void*)(scr->workspace_count-1));
139 XFlush(dpy);
141 return scr->workspace_count-1;
144 return -1;
148 Bool
149 wWorkspaceDelete(WScreen *scr, int workspace)
151 WWindow *tmp;
152 WWorkspace **list;
153 int i, j;
155 if (workspace<=0)
156 return False;
158 /* verify if workspace is in use by some window */
159 tmp = scr->focused_window;
160 while (tmp) {
161 if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace==workspace)
162 return False;
163 tmp = tmp->prev;
166 if (!wPreferences.flags.noclip) {
167 wDockDestroy(scr->workspaces[workspace]->clip);
168 scr->workspaces[workspace]->clip = NULL;
171 list = wmalloc(sizeof(WWorkspace*)*(scr->workspace_count-1));
172 j = 0;
173 for (i=0; i<scr->workspace_count; i++) {
174 if (i!=workspace) {
175 list[j++] = scr->workspaces[i];
176 } else {
177 if (scr->workspaces[i]->name)
178 wfree(scr->workspaces[i]->name);
179 wfree(scr->workspaces[i]);
182 wfree(scr->workspaces);
183 scr->workspaces = list;
185 scr->workspace_count--;
188 /* update menu */
189 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
190 /* clip workspace menu */
191 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
193 /* update also window menu */
194 if (scr->workspace_submenu) {
195 WMenu *menu = scr->workspace_submenu;
197 i = menu->entry_no;
198 while (i>scr->workspace_count)
199 wMenuRemoveItem(menu, --i);
200 wMenuRealize(menu);
202 /* and clip menu */
203 if (scr->clip_submenu) {
204 WMenu *menu = scr->clip_submenu;
206 i = menu->entry_no;
207 while (i>scr->workspace_count)
208 wMenuRemoveItem(menu, --i);
209 wMenuRealize(menu);
212 #ifdef NETWM_HINTS
213 wNETWMUpdateDesktop(scr);
214 #endif
216 WMPostNotificationName(WMNWorkspaceDestroyed, scr,
217 (void*)(scr->workspace_count-1));
219 if (scr->current_workspace >= scr->workspace_count)
220 wWorkspaceChange(scr, scr->workspace_count-1);
222 return True;
226 typedef struct WorkspaceNameData {
227 int count;
228 RImage *back;
229 RImage *text;
230 time_t timeout;
231 } WorkspaceNameData;
234 static void
235 hideWorkspaceName(void *data)
237 WScreen *scr = (WScreen*)data;
239 if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
240 || time(NULL) > scr->workspace_name_data->timeout) {
241 XUnmapWindow(dpy, scr->workspace_name);
243 if (scr->workspace_name_data) {
244 RReleaseImage(scr->workspace_name_data->back);
245 RReleaseImage(scr->workspace_name_data->text);
246 wfree(scr->workspace_name_data);
248 scr->workspace_name_data = NULL;
250 scr->workspace_name_timer = NULL;
251 } else {
252 RImage *img = RCloneImage(scr->workspace_name_data->back);
253 Pixmap pix;
255 scr->workspace_name_timer =
256 WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
258 RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
259 scr->workspace_name_data->count*255/10);
261 RConvertImage(scr->rcontext, img, &pix);
263 RReleaseImage(img);
265 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
266 XClearWindow(dpy, scr->workspace_name);
267 XFreePixmap(dpy, pix);
268 XFlush(dpy);
270 scr->workspace_name_data->count--;
275 static void
276 showWorkspaceName(WScreen *scr, int workspace)
278 WorkspaceNameData *data;
279 RXImage *ximg;
280 Pixmap text, mask;
281 int w, h;
282 int px, py;
283 char *name = scr->workspaces[workspace]->name;
284 int len = strlen(name);
285 int x, y;
287 if (wPreferences.workspace_name_display_position == WD_NONE ||
288 scr->workspace_count < 2) {
289 return;
292 if (scr->workspace_name_timer) {
293 WMDeleteTimerHandler(scr->workspace_name_timer);
294 XUnmapWindow(dpy, scr->workspace_name);
295 XFlush(dpy);
297 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY,
298 hideWorkspaceName, scr);
300 if (scr->workspace_name_data) {
301 RReleaseImage(scr->workspace_name_data->back);
302 RReleaseImage(scr->workspace_name_data->text);
303 wfree(scr->workspace_name_data);
306 data = wmalloc(sizeof(WorkspaceNameData));
307 data->back = NULL;
309 w = WMWidthOfString(scr->workspace_name_font, name, len);
310 h = WMFontHeight(scr->workspace_name_font);
312 switch (wPreferences.workspace_name_display_position) {
313 case WD_TOP:
314 px = (scr->scr_width - (w+4))/2;
315 py = 0;
316 break;
317 case WD_BOTTOM:
318 px = (scr->scr_width - (w+4))/2;
319 py = scr->scr_height - (h+4);
320 break;
321 case WD_TOPLEFT:
322 px = 0;
323 py = 0;
324 break;
325 case WD_TOPRIGHT:
326 px = scr->scr_width - (w+4);
327 py = 0;
328 break;
329 case WD_BOTTOMLEFT:
330 px = 0;
331 py = scr->scr_height - (h+4);
332 break;
333 case WD_BOTTOMRIGHT:
334 px = scr->scr_width - (w+4);
335 py = scr->scr_height - (h+4);
336 break;
337 case WD_CENTER:
338 default:
339 px = (scr->scr_width - (w+4))/2;
340 py = (scr->scr_height - (h+4))/2;
341 break;
343 XResizeWindow(dpy, scr->workspace_name, w+4, h+4);
344 XMoveWindow(dpy, scr->workspace_name, px, py);
346 text = XCreatePixmap(dpy, scr->w_win, w+4, h+4, scr->w_depth);
347 mask = XCreatePixmap(dpy, scr->w_win, w+4, h+4, 1);
349 /*XSetForeground(dpy, scr->mono_gc, 0);
350 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4);*/
352 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w+4, h+4);
354 for (x = 0; x <= 4; x++) {
355 for (y = 0; y <= 4; y++) {
356 WMDrawString(scr->wmscreen, text, scr->white,
357 scr->workspace_name_font, x, y, name, len);
361 XSetForeground(dpy, scr->mono_gc, 1);
362 XSetBackground(dpy, scr->mono_gc, 0);
364 XCopyPlane(dpy, text, mask, scr->mono_gc, 0, 0, w+4, h+4, 0, 0, 1<<(scr->w_depth-1));
366 /*XSetForeground(dpy, scr->mono_gc, 1);*/
367 XSetBackground(dpy, scr->mono_gc, 1);
369 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w+4, h+4);
371 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font,
372 2, 2, name, len);
374 #ifdef SHAPE
375 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask,
376 ShapeSet);
377 #endif
378 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
379 XClearWindow(dpy, scr->workspace_name);
381 data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
383 XFreePixmap(dpy, text);
384 XFreePixmap(dpy, mask);
386 if (!data->text) {
387 XMapRaised(dpy, scr->workspace_name);
388 XFlush(dpy);
390 goto erro;
393 ximg = RGetXImage(scr->rcontext, scr->root_win, px, py,
394 data->text->width, data->text->height);
396 if (!ximg || !ximg->image) {
397 goto erro;
400 XMapRaised(dpy, scr->workspace_name);
401 XFlush(dpy);
403 data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
404 RDestroyXImage(scr->rcontext, ximg);
406 if (!data->back) {
407 goto erro;
410 data->count = 10;
412 /* set a timeout for the effect */
413 data->timeout = time(NULL) + 2 +
414 (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY*data->count)/1000;
416 scr->workspace_name_data = data;
419 return;
421 erro:
422 if (scr->workspace_name_timer)
423 WMDeleteTimerHandler(scr->workspace_name_timer);
425 if (data->text)
426 RReleaseImage(data->text);
427 if (data->back)
428 RReleaseImage(data->back);
429 wfree(data);
431 scr->workspace_name_data = NULL;
433 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
434 10*WORKSPACE_NAME_FADE_DELAY,
435 hideWorkspaceName, scr);
439 void
440 wWorkspaceChange(WScreen *scr, int workspace)
442 if (scr->flags.startup || scr->flags.startup2) {
443 return;
446 if (workspace != scr->current_workspace) {
447 wWorkspaceForceChange(scr, workspace);
448 } /*else {
449 showWorkspaceName(scr, workspace);
454 void
455 wWorkspaceRelativeChange(WScreen *scr, int amount)
457 int w;
459 w = scr->current_workspace + amount;
461 if (amount < 0) {
462 if (w >= 0) {
463 wWorkspaceChange(scr, w);
464 } else if (wPreferences.ws_cycle) {
465 wWorkspaceChange(scr, scr->workspace_count + w);
467 } else if (amount > 0) {
468 if (w < scr->workspace_count) {
469 wWorkspaceChange(scr, w);
470 } else if (wPreferences.ws_advance) {
471 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES-1));
472 } else if (wPreferences.ws_cycle) {
473 wWorkspaceChange(scr, w % scr->workspace_count);
479 void
480 wWorkspaceForceChange(WScreen *scr, int workspace)
482 WWindow *tmp, *foc=NULL, *foc2=NULL;
484 if (workspace >= MAX_WORKSPACES || workspace < 0)
485 return;
487 SendHelperMessage(scr, 'C', workspace+1, NULL);
489 if (workspace > scr->workspace_count-1) {
490 wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
493 wClipUpdateForWorkspaceChange(scr, workspace);
495 scr->current_workspace = workspace;
497 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
499 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
501 if ((tmp = scr->focused_window)!= NULL) {
502 if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded) &&
503 !WFLAGP(tmp, no_focusable)) || tmp->flags.changing_workspace) {
504 foc = tmp;
507 /* foc2 = tmp; will fix annoyance with gnome panel
508 * but will create annoyance for every other application
510 while (tmp) {
511 if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
512 /* unmap windows not on this workspace */
513 if ((tmp->flags.mapped||tmp->flags.shaded) &&
514 !IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) {
515 wWindowUnmap(tmp);
517 /* also unmap miniwindows not on this workspace */
518 if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
519 tmp->icon && !IS_OMNIPRESENT(tmp)) {
520 XUnmapWindow(dpy, tmp->icon->core->window);
521 tmp->icon->mapped = 0;
523 /* update current workspace of omnipresent windows */
524 if (IS_OMNIPRESENT(tmp)) {
525 WApplication *wapp = wApplicationOf(tmp->main_window);
527 tmp->frame->workspace = workspace;
529 if (wapp) {
530 wapp->last_workspace = workspace;
532 if (!foc2 && (tmp->flags.mapped || tmp->flags.shaded)) {
533 foc2 = tmp;
536 } else {
537 /* change selected windows' workspace */
538 if (tmp->flags.selected) {
539 wWindowChangeWorkspace(tmp, workspace);
540 if (!tmp->flags.miniaturized && !foc) {
541 foc = tmp;
543 } else {
544 if (!tmp->flags.hidden) {
545 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
546 /* remap windows that are on this workspace */
547 wWindowMap(tmp);
548 if (!foc && !WFLAGP(tmp, no_focusable)) {
549 foc = tmp;
552 /* Also map miniwindow if not omnipresent */
553 if (!wPreferences.sticky_icons &&
554 tmp->flags.miniaturized &&
555 !IS_OMNIPRESENT(tmp) && tmp->icon) {
556 tmp->icon->mapped = 1;
557 XMapWindow(dpy, tmp->icon->core->window);
562 tmp = tmp->prev;
565 /* Gobble up events unleashed by our mapping & unmapping.
566 * These may trigger various grab-initiated focus &
567 * crossing events. However, we don't care about them,
568 * and ignore their focus implications altogether to avoid
569 * flicker.
571 scr->flags.ignore_focus_events = 1;
572 ProcessPendingEvents();
573 scr->flags.ignore_focus_events = 0;
575 if (!foc)
576 foc = foc2;
578 if (scr->focused_window->flags.mapped && !foc) {
579 foc = scr->focused_window;
581 if (wPreferences.focus_mode == WKF_CLICK) {
582 wSetFocusTo(scr, foc);
583 } else {
584 unsigned int mask;
585 int foo;
586 Window bar, win;
587 WWindow *tmp;
589 tmp = NULL;
590 if (XQueryPointer(dpy, scr->root_win, &bar, &win,
591 &foo, &foo, &foo, &foo, &mask)) {
592 tmp = wWindowFor(win);
595 /* If there's a window under the pointer, focus it.
596 * (we ate all other focus events above, so it's
597 * certainly not focused). Otherwise focus last
598 * focused, or the root (depending on sloppiness)
600 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
601 wSetFocusTo(scr, foc);
602 } else {
603 wSetFocusTo(scr, tmp);
608 /* We need to always arrange icons when changing workspace, even if
609 * no autoarrange icons, because else the icons in different workspaces
610 * can be superposed.
611 * This can be avoided if appicons are also workspace specific.
613 if (!wPreferences.sticky_icons)
614 wArrangeIcons(scr, False);
616 if (scr->dock)
617 wAppIconPaint(scr->dock->icon_array[0]);
619 if (scr->clip_icon) {
620 if (scr->workspaces[workspace]->clip->auto_collapse ||
621 scr->workspaces[workspace]->clip->auto_raise_lower) {
622 /* to handle enter notify. This will also */
623 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
624 XMapWindow(dpy, scr->clip_icon->icon->core->window);
625 } else {
626 wClipIconPaint(scr->clip_icon);
630 #ifdef NETWM_HINTS
631 wScreenUpdateUsableArea(scr);
632 wNETWMUpdateDesktop(scr);
633 #endif
635 showWorkspaceName(scr, workspace);
637 WMPostNotificationName(WMNWorkspaceChanged, scr, (void*)workspace);
639 /* XSync(dpy, False); */
643 #ifdef VIRTUAL_DESKTOP
645 /* TODO:
647 * 1) Allow border around each window so the scrolling
648 * won't just stop at the border.
649 * 2) Make pager.
653 #define vec_sub(a, b) wmkpoint((a).x-(b).x, (a).y-(b).y)
654 #define vec_add(a, b) wmkpoint((a).x+(b).x, (a).y+(b).y)
655 #define vec_inc(a, b) do { (a).x+=(b).x; (a).y+=(b).y; } while(0)
656 #define vec_dot(a, b) ((a).x*(b).x + (a).y*(b).y)
657 #define vec_scale(a, s) wmkpoint((a).x*s, (a).y*s)
658 #define vec_scale2(a, s, t) wmkpoint((a).x*s, (a).y*t)
660 #ifndef HAS_BORDER
661 #define HAS_BORDER(w) (!(WFLAGP((w), no_border)))
662 #endif
664 #ifndef IS_VSTUCK
665 #define IS_VSTUCK(w) (WFLAGP((w), virtual_stick))
666 #endif
668 #define updateMinimum(l,p,ml,mp) do { if (cmp(l) && (l)<(ml)) { (ml)=(l); (mp)=(p); }; } while(0)
670 static Bool cmp_gez(int i) { return (i >= 0); }
672 static Bool cmp_gz(int i) { return (i > 0); }
675 static WMPoint
676 getClosestEdge(WScreen * scr, WMPoint direction, Bool (*cmp)(int))
678 WMPoint closest = wmkpoint(0, 0);
679 int closest_len = INT_MAX;
680 WWindow * wwin;
682 for (wwin=scr->focused_window; wwin; wwin=wwin->prev) {
683 if (wwin->frame->workspace == scr->current_workspace) {
684 if (!wwin->flags.miniaturized &&
685 !IS_VSTUCK(wwin) &&
686 !wwin->flags.hidden) {
687 int border = 2*HAS_BORDER(wwin);
688 int len;
689 int x1,x2,y1,y2;
690 int head = wGetHeadForWindow(wwin);
691 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
692 WMPoint p;
694 x1 = wwin->frame_x - area.x1;
695 y1 = wwin->frame_y - area.y1;
696 x2 = wwin->frame_x + wwin->frame->core->width + border - area.x2;
697 y2 = wwin->frame_y + wwin->frame->core->height + border - area.y2;
699 p = wmkpoint(x1,y1);
700 len = vec_dot(direction, p);
701 updateMinimum(len, p, closest_len, closest);
703 p = wmkpoint(x1,y2);
704 len = vec_dot(direction, p);
705 updateMinimum(len, p, closest_len, closest);
707 p = wmkpoint(x2,y1);
708 len = vec_dot(direction, p);
709 updateMinimum(len, p, closest_len, closest);
711 p = wmkpoint(x2,y2);
712 len = vec_dot(direction, p);
713 updateMinimum(len, p, closest_len, closest);
718 return closest;
722 void
723 wWorkspaceKeyboardMoveDesktop(WScreen *scr, WMPoint direction)
725 int x, y;
726 WMPoint edge = getClosestEdge(scr, direction, cmp_gz);
727 int len = vec_dot(edge, direction);
728 WMPoint step = vec_scale(direction, len);
729 wWorkspaceGetViewPosition(scr, scr->current_workspace, &x, &y);
730 wWorkspaceSetViewPort(scr, scr->current_workspace, x+step.x, y+step.y);
734 extern Cursor wCursor[WCUR_LAST];
737 static void
738 vdMouseMoveDesktop(XEvent *event, WMPoint direction)
740 static int lock = False;
741 if (lock) return;
742 lock = True;
744 Bool done = False;
745 Bool moved = True;
746 WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
747 WMPoint old_pos = wmkpoint(event->xcrossing.x_root, event->xcrossing.y_root);
748 WMPoint step;
749 int x, y;
750 int resisted = 0;
752 if (XGrabPointer(dpy, event->xcrossing.window, False,
753 PointerMotionMask, GrabModeAsync, GrabModeAsync,
754 scr->root_win, wCursor[WCUR_EMPTY],
755 CurrentTime) != GrabSuccess) {
757 /* if the grab fails, do it the old fashioned way */
758 step = vec_scale2(direction, wPreferences.vedge_hscrollspeed,
759 wPreferences.vedge_vscrollspeed);
760 wWorkspaceGetViewPosition(scr, scr->current_workspace, &x, &y);
761 if (wWorkspaceSetViewPort(scr, scr->current_workspace,
762 x+step.x, y+step.y)) {
763 step = vec_scale(direction, wPreferences.vedge_thickness + 1);
764 XWarpPointer(dpy, None, scr->root_win, 0,0,0,0,
765 event->xcrossing.x_root - step.x,
766 event->xcrossing.y_root - step.y);
768 goto exit;
770 XSync(dpy, True);
772 if (old_pos.x < 0)
773 old_pos.x = 0;
774 if (old_pos.y < 0)
775 old_pos.y = 0;
776 if (old_pos.x > scr->scr_width)
777 old_pos.x = scr->scr_width;
778 if (old_pos.y > scr->scr_height)
779 old_pos.y = scr->scr_height;
781 while (!done) {
782 XEvent ev;
783 if (moved) {
784 XWarpPointer(dpy, None, scr->root_win, 0, 0, 0, 0,
785 scr->scr_width/2, scr->scr_height/2);
786 moved = False;
788 WMMaskEvent(dpy, PointerMotionMask, &ev);
790 switch (ev.type) {
791 case MotionNotify:
793 int step_len;
794 step = wmkpoint(ev.xmotion.x_root-scr->scr_width/2,
795 ev.xmotion.y_root-scr->scr_height/2);
796 step_len = vec_dot(step, direction);
797 if (step_len < 0) {
798 done = True;
799 break;
802 if (step_len > 0) {
803 Bool do_move = True;
804 int resist = wPreferences.vedge_resistance;
805 WMPoint closest;
806 int closest_len = INT_MAX;
807 if (resist) {
808 closest = getClosestEdge(scr, direction, cmp_gez);
809 closest_len = vec_dot(direction, closest);
812 if (!closest_len) {
813 resisted += step_len;
814 do_move = resisted >= resist;
815 if (do_move) {
816 closest_len = INT_MAX;
817 step_len = resisted - resist;
818 resisted = 0;
821 if (do_move) {
822 if (closest_len <= wPreferences.vedge_attraction) {
823 step = vec_scale(direction, closest_len);
824 } else {
825 step = vec_scale(direction, step_len);
828 wWorkspaceGetViewPosition(scr, scr->current_workspace, &x, &y);
829 wWorkspaceSetViewPort(scr, scr->current_workspace,
830 x+step.x, y+step.y);
831 moved = True;
835 break;
839 step = vec_add(old_pos, vec_scale(direction, -1));
840 XWarpPointer(dpy, None, scr->root_win, 0,0,0,0, step.x, step.y);
841 XUngrabPointer(dpy, CurrentTime);
843 exit:
844 lock = False;
848 static void
849 vdHandleEnter_u(XEvent *event) {
850 vdMouseMoveDesktop(event, VEC_UP);
854 static void
855 vdHandleEnter_d(XEvent *event) {
856 vdMouseMoveDesktop(event, VEC_DOWN);
860 static void
861 vdHandleEnter_l(XEvent *event) {
862 vdMouseMoveDesktop(event, VEC_LEFT);
866 static void
867 vdHandleEnter_r(XEvent *event) {
868 vdMouseMoveDesktop(event, VEC_RIGHT);
872 static void
873 wWorkspaceMapEdge(WScreen *scr)
875 int i;
876 if (wPreferences.vedge_thickness && initVDesk) {
877 for (i=0; i<scr->virtual_nr_edges; ++i) {
878 XMapWindow(dpy, scr->virtual_edges[i]);
884 static void
885 wWorkspaceUnmapEdge(WScreen *scr)
887 int i;
888 if (wPreferences.vedge_thickness && initVDesk) {
889 for (i=0; i<scr->virtual_nr_edges; ++i) {
890 XUnmapWindow(dpy, scr->virtual_edges[i]);
896 #define LEFT_EDGE 0x01
897 #define RIGHT_EDGE 0x02
898 #define TOP_EDGE 0x04
899 #define BOTTOM_EDGE 0x08
900 #define ALL_EDGES 0x0F
902 void
903 wWorkspaceManageEdge(WScreen *scr)
905 if (!initVDesk && wPreferences.vedge_thickness) {
906 int i, j, w;
907 int vmask;
908 XSetWindowAttributes attribs;
910 int heads = wXineramaHeads(scr);
911 int *hasEdges = (int*)wmalloc(sizeof(int)*heads);
913 int thickness = wPreferences.vedge_thickness;
914 int nr_edges = 0;
915 int max_edges = 4*heads;
916 int head;
917 Window * edges = (Window *)wmalloc(sizeof(Window)*max_edges);
919 initVDesk = True;
921 for (i=0; i<heads; ++i)
922 hasEdges[i] = ALL_EDGES;
923 for (i=0; i<heads; ++i) {
924 WMRect i_rect = wGetRectForHead(scr, i);
925 for (j=i+1; j<heads; ++j) {
926 WMRect j_rect = wGetRectForHead(scr, j);
928 int vlen = (WMIN(i_rect.pos.y + i_rect.size.height,
929 j_rect.pos.y + j_rect.size.height) -
930 WMAX(i_rect.pos.y, j_rect.pos.y));
932 int hlen = (WMIN(i_rect.pos.x + i_rect.size.width,
933 j_rect.pos.x + j_rect.size.width) -
934 WMAX( i_rect.pos.x, j_rect.pos.x));
936 if (vlen > 0 && hlen == 0) { /* horz alignment, vert edges touch */
937 if (i_rect.pos.x < j_rect.pos.x) { /* i left of j */
938 hasEdges[i] &= ~RIGHT_EDGE;
939 hasEdges[j] &= ~LEFT_EDGE;
940 } else { /* j left of i */
941 hasEdges[j] &= ~RIGHT_EDGE;
942 hasEdges[i] &= ~LEFT_EDGE;
944 } else if (vlen == 0 && hlen > 0) { /* vert alignment, horz edges touch */
945 if (i_rect.pos.y < j_rect.pos.y) { /* i top of j */
946 hasEdges[i] &= ~BOTTOM_EDGE;
947 hasEdges[j] &= ~TOP_EDGE;
948 } else { /* j top of i */
949 hasEdges[j] &= ~BOTTOM_EDGE;
950 hasEdges[i] &= ~TOP_EDGE;
956 for (w = 0; w < scr->workspace_count; w++) {
957 /* puts("reset workspace"); */
958 wWorkspaceSetViewPort(scr, w, 0, 0);
961 vmask = CWEventMask|CWOverrideRedirect;
962 attribs.event_mask = (EnterWindowMask | LeaveWindowMask | VisibilityChangeMask);
963 attribs.override_redirect = True;
965 for (head=0; head<wXineramaHeads(scr); ++head) {
966 WMRect rect = wGetRectForHead(scr, head);
968 if (hasEdges[head] & TOP_EDGE) {
969 edges[nr_edges] =
970 XCreateWindow(dpy, scr->root_win, rect.pos.x, rect.pos.y,
971 rect.size.width, thickness, 0,
972 CopyFromParent, InputOnly, CopyFromParent,
973 vmask, &attribs);
974 XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
975 (XPointer)vdHandleEnter_u);
976 ++nr_edges;
979 if (hasEdges[head] & BOTTOM_EDGE) {
980 edges[nr_edges] =
981 XCreateWindow(dpy, scr->root_win, rect.pos.x,
982 rect.pos.y+rect.size.height-thickness,
983 rect.size.width, thickness, 0,
984 CopyFromParent, InputOnly, CopyFromParent,
985 vmask, &attribs);
986 XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
987 (XPointer)vdHandleEnter_d);
988 ++nr_edges;
991 if (hasEdges[head] & LEFT_EDGE) {
992 edges[nr_edges] =
993 XCreateWindow(dpy, scr->root_win, rect.pos.x, rect.pos.y,
994 thickness, rect.pos.y+rect.size.height, 0,
995 CopyFromParent, InputOnly, CopyFromParent,
996 vmask, &attribs);
997 XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
998 (XPointer)vdHandleEnter_l);
999 ++nr_edges;
1002 if (hasEdges[head] & RIGHT_EDGE) {
1003 edges[nr_edges] =
1004 XCreateWindow(dpy, scr->root_win,
1005 rect.pos.x + rect.size.width - thickness, rect.pos.y,
1006 thickness, rect.size.height, 0,
1007 CopyFromParent, InputOnly, CopyFromParent, vmask,
1008 &attribs);
1009 XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
1010 (XPointer)vdHandleEnter_r);
1011 ++nr_edges;
1015 scr->virtual_nr_edges = nr_edges;
1016 scr->virtual_edges = edges;
1018 wWorkspaceMapEdge(scr);
1019 wWorkspaceRaiseEdge(scr);
1021 wfree(hasEdges);
1026 void
1027 wWorkspaceUpdateEdge(WScreen *scr)
1029 if (!initVDesk && wPreferences.vedge_thickness) {
1030 wWorkspaceManageEdge(scr);
1031 } else if (initVDesk) {
1032 if (wPreferences.vedge_thickness) {
1033 wWorkspaceMapEdge(scr);
1034 } else {
1035 wWorkspaceUnmapEdge(scr);
1041 void
1042 wWorkspaceDestroyEdge(WScreen *scr)
1044 if (!initVDesk) {
1045 int i;
1047 for (i=0; i<scr->virtual_nr_edges; ++i) {
1048 XDeleteContext(dpy, scr->virtual_edges[i], wVEdgeContext);
1049 XDestroyWindow(dpy, scr->virtual_edges[i]);
1052 wfree(scr->virtual_edges);
1053 scr->virtual_edges = NULL;
1054 scr->virtual_nr_edges = 0;
1056 initVDesk = False;
1061 void
1062 wWorkspaceRaiseEdge(WScreen *scr)
1064 static int toggle = 0;
1065 int i;
1067 if (wPreferences.vedge_thickness && initVDesk) {
1068 if (toggle) {
1069 for (i=0; i<scr->virtual_nr_edges; ++i) {
1070 XRaiseWindow(dpy, scr->virtual_edges[i]);
1072 } else {
1073 for (i=scr->virtual_nr_edges-1; i>=0; --i) {
1074 XRaiseWindow(dpy, scr->virtual_edges[i]);
1078 toggle ^= 1;
1082 void
1083 wWorkspaceLowerEdge(WScreen *scr)
1085 int i;
1086 if (wPreferences.vedge_thickness && initVDesk) {
1087 for (i=0; i<scr->virtual_nr_edges; ++i) {
1088 XLowerWindow(dpy, scr->virtual_edges[i]);
1094 void
1095 wWorkspaceResizeViewPort(WScreen *scr, int workspace)
1097 int x, y;
1098 wWorkspaceGetViewPosition(scr, scr->current_workspace, &x, &y);
1099 wWorkspaceSetViewPort(scr, scr->current_workspace, x, y);
1103 void
1104 updateWorkspaceGeometry(WScreen *scr, int workspace, int *view_x, int *view_y)
1106 int most_left, most_right, most_top, most_bottom;
1107 WWindow *wwin;
1109 int heads = wXineramaHeads(scr);
1110 typedef int strut_t[4];
1111 strut_t * strut = (strut_t*)wmalloc(heads*sizeof(strut_t));
1112 int head, i;
1114 for (head=0; head<heads; ++head) {
1115 WMRect rect = wGetRectForHead(scr, head);
1116 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
1117 strut[head][0] = area.x1 - rect.pos.x;
1118 strut[head][1] = rect.pos.x + rect.size.width - area.x2;
1119 strut[head][2] = area.y1 - rect.pos.y;
1120 strut[head][3] = rect.pos.y + rect.size.height - area.y2;
1123 /* adjust workspace layout */
1124 wwin = scr->focused_window;
1125 most_right = 0;
1126 most_bottom = 0;
1127 most_left = scr->scr_width;
1128 most_top = scr->scr_height;
1129 for(;wwin; wwin = wwin->prev) {
1130 if (wwin->frame->workspace == workspace) {
1131 if (!wwin->flags.miniaturized && !IS_VSTUCK(wwin) &&
1132 !wwin->flags.hidden) {
1134 head = wGetHeadForWindow(wwin);
1136 i = wwin->frame_x - strut[head][0];
1137 if (i < most_left) /* record positions, should this be cached? */
1138 most_left = i;
1139 i = wwin->frame_x + wwin->frame->core->width + strut[head][1];
1140 if (HAS_BORDER(wwin))
1141 i+=2;
1142 if (i > most_right)
1143 most_right = i;
1144 i = wwin->frame_y - strut[head][2];
1145 if (i < most_top)
1146 most_top = i;
1147 i = wwin->frame_y + wwin->frame->core->height + strut[head][3];
1148 if (HAS_BORDER(wwin))
1149 i+=2;
1150 if (i > most_bottom) {
1151 most_bottom = i;
1157 if (most_left > 0) most_left = 0;
1158 if (most_top > 0) most_top = 0;
1160 scr->workspaces[workspace]->width = WMAX(most_right, scr->scr_width) - WMIN(most_left, 0);
1161 scr->workspaces[workspace]->height = WMAX(most_bottom, scr->scr_height) - WMIN(most_top, 0);
1163 *view_x += -most_left - scr->workspaces[workspace]->view_x;
1164 scr->workspaces[workspace]->view_x = -most_left;
1166 *view_y += -most_top - scr->workspaces[workspace]->view_y;
1167 scr->workspaces[workspace]->view_y = -most_top;
1169 wfree(strut);
1173 typedef struct _delay_configure {
1174 WWindow *wwin;
1175 int delay_count;
1176 } _delay_configure;
1179 void
1180 sendConfigureNotify (_delay_configure *delay)
1182 WWindow *wwin;
1184 delay->delay_count--;
1185 if (!delay->delay_count) {
1186 for (wwin = delay->wwin; wwin; wwin = wwin->prev) {
1187 wWindowSynthConfigureNotify(wwin);
1193 Bool
1194 wWorkspaceSetViewPort(WScreen *scr, int workspace, int view_x, int view_y)
1196 Bool adjust_flag = False;
1197 int diff_x, diff_y;
1198 static _delay_configure delay_configure = {NULL, 0};
1199 WWorkspace *wptr;
1200 WWindow *wwin;
1202 wptr = scr->workspaces[workspace];
1204 /*printf("wWorkspaceSetViewPort %d %d\n", view_x, view_y);*/
1206 updateWorkspaceGeometry(scr, workspace, &view_x, &view_y);
1208 if (view_x + scr->scr_width > wptr->width) {
1209 /* puts("right edge of vdesk"); */
1210 view_x = wptr->width - scr->scr_width;
1212 if (view_x < 0) {
1213 /* puts("left edge of vdesk"); */
1214 view_x = 0;
1216 if (view_y + scr->scr_height > wptr->height) {
1217 /* puts("right edge of vdesk"); */
1218 view_y = wptr->height - scr->scr_height;
1220 if (view_y < 0) {
1221 /* puts("left edge of vdesk"); */
1222 view_y = 0;
1225 diff_x = wptr->view_x - view_x;
1226 diff_y = wptr->view_y - view_y;
1227 if (!diff_x && !diff_y)
1228 return False;
1230 wptr->view_x = view_x;
1231 wptr->view_y = view_y;
1233 #ifdef NETWM_HINTS
1234 wNETWMUpdateDesktop(scr);
1235 #endif
1237 for (wwin = scr->focused_window; wwin; wwin = wwin->prev) {
1238 if (wwin->frame->workspace == workspace && !IS_VSTUCK(wwin)) {
1239 wWindowMove(wwin, wwin->frame_x + diff_x, wwin->frame_y + diff_y);
1240 adjust_flag = True;
1243 if (1) { /* if delay*/
1244 delay_configure.delay_count++;
1245 delay_configure.wwin = scr->focused_window;
1246 WMAddTimerHandler(200, (WMCallback *)sendConfigureNotify, &delay_configure);
1249 return adjust_flag;
1253 void
1254 wWorkspaceGetViewPosition(WScreen *scr, int workspace, int *x, int *y)
1256 *x = scr->workspaces[workspace]->view_x;
1257 *y = scr->workspaces[workspace]->view_y;
1259 #endif
1262 static void
1263 switchWSCommand(WMenu *menu, WMenuEntry *entry)
1265 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
1269 static void
1270 deleteWSCommand(WMenu *menu, WMenuEntry *entry)
1272 wWorkspaceDelete(menu->frame->screen_ptr,
1273 menu->frame->screen_ptr->workspace_count-1);
1277 static void
1278 newWSCommand(WMenu *menu, WMenuEntry *foo)
1280 int ws;
1282 ws = wWorkspaceNew(menu->frame->screen_ptr);
1283 /* autochange workspace*/
1284 if (ws>=0)
1285 wWorkspaceChange(menu->frame->screen_ptr, ws);
1289 if (ws<9) {
1290 int kcode;
1291 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
1292 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
1293 entry->rtext =
1294 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
1300 static char*
1301 cropline(char *line)
1303 char *start, *end;
1305 if (strlen(line)==0)
1306 return line;
1308 start = line;
1309 end = &(line[strlen(line)])-1;
1310 while (isspace(*line) && *line!=0) line++;
1311 while (isspace(*end) && end!=line) {
1312 *end=0;
1313 end--;
1315 return line;
1319 void
1320 wWorkspaceRename(WScreen *scr, int workspace, char *name)
1322 char buf[MAX_WORKSPACENAME_WIDTH+1];
1323 char *tmp;
1325 if (workspace >= scr->workspace_count)
1326 return;
1328 /* trim white spaces */
1329 tmp = cropline(name);
1331 if (strlen(tmp)==0) {
1332 sprintf(buf, _("Workspace %i"), workspace+1);
1333 } else {
1334 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
1336 buf[MAX_WORKSPACENAME_WIDTH] = 0;
1338 /* update workspace */
1339 wfree(scr->workspaces[workspace]->name);
1340 scr->workspaces[workspace]->name = wstrdup(buf);
1342 if (scr->clip_ws_menu) {
1343 if (strcmp(scr->clip_ws_menu->entries[workspace+2]->text, buf)!=0) {
1344 wfree(scr->clip_ws_menu->entries[workspace+2]->text);
1345 scr->clip_ws_menu->entries[workspace+2]->text = wstrdup(buf);
1346 wMenuRealize(scr->clip_ws_menu);
1349 if (scr->workspace_menu) {
1350 if (strcmp(scr->workspace_menu->entries[workspace+2]->text, buf)!=0) {
1351 wfree(scr->workspace_menu->entries[workspace+2]->text);
1352 scr->workspace_menu->entries[workspace+2]->text = wstrdup(buf);
1353 wMenuRealize(scr->workspace_menu);
1357 if (scr->clip_icon)
1358 wClipIconPaint(scr->clip_icon);
1360 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)workspace);
1366 /* callback for when menu entry is edited */
1367 static void
1368 onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
1370 char *tmp;
1372 tmp = entry->text;
1373 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
1377 WMenu*
1378 wWorkspaceMenuMake(WScreen *scr, Bool titled)
1380 WMenu *wsmenu;
1382 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
1383 if (!wsmenu) {
1384 wwarning(_("could not create Workspace menu"));
1385 return NULL;
1388 /* callback to be called when an entry is edited */
1389 wsmenu->on_edit = onMenuEntryEdited;
1391 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
1392 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
1394 return wsmenu;
1399 void
1400 wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
1402 int i;
1403 long ws;
1404 char title[MAX_WORKSPACENAME_WIDTH+1];
1405 WMenuEntry *entry;
1406 int tmp;
1408 if (!menu)
1409 return;
1411 if (menu->entry_no < scr->workspace_count+2) {
1412 /* new workspace(s) added */
1413 i = scr->workspace_count-(menu->entry_no-2);
1414 ws = menu->entry_no - 2;
1415 while (i>0) {
1416 strcpy(title, scr->workspaces[ws]->name);
1418 entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
1419 entry->flags.indicator = 1;
1420 entry->flags.editable = 1;
1422 i--;
1423 ws++;
1425 } else if (menu->entry_no > scr->workspace_count+2) {
1426 /* removed workspace(s) */
1427 for (i = menu->entry_no-1; i >= scr->workspace_count+2; i--) {
1428 wMenuRemoveItem(menu, i);
1431 wMenuRealize(menu);
1433 for (i=0; i<scr->workspace_count; i++) {
1434 menu->entries[i+2]->flags.indicator_on = 0;
1436 menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;
1438 /* don't let user destroy current workspace */
1439 if (scr->current_workspace == scr->workspace_count-1) {
1440 wMenuSetEnabled(menu, 1, False);
1441 } else {
1442 wMenuSetEnabled(menu, 1, True);
1445 tmp = menu->frame->top_width + 5;
1446 /* if menu got unreachable, bring it to a visible place */
1447 if (menu->frame_x < tmp - (int)menu->frame->core->width)
1448 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
1450 wMenuPaint(menu);
1454 void
1455 wWorkspaceSaveState(WScreen *scr, WMPropList *old_state)
1457 WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
1458 int i;
1460 make_keys();
1462 old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
1463 parr = WMCreatePLArray(NULL);
1464 for (i=0; i < scr->workspace_count; i++) {
1465 pstr = WMCreatePLString(scr->workspaces[i]->name);
1466 wks_state = WMCreatePLDictionary(dName, pstr, NULL);
1467 WMReleasePropList(pstr);
1468 if (!wPreferences.flags.noclip) {
1469 pstr = wClipSaveWorkspaceState(scr, i);
1470 WMPutInPLDictionary(wks_state, dClip, pstr);
1471 WMReleasePropList(pstr);
1472 } else if (old_wks_state!=NULL) {
1473 if ((foo = WMGetFromPLArray(old_wks_state, i))!=NULL) {
1474 if ((bar = WMGetFromPLDictionary(foo, dClip))!=NULL) {
1475 WMPutInPLDictionary(wks_state, dClip, bar);
1479 WMAddToPLArray(parr, wks_state);
1480 WMReleasePropList(wks_state);
1482 WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
1483 WMReleasePropList(parr);
1487 void
1488 wWorkspaceRestoreState(WScreen *scr)
1490 WMPropList *parr, *pstr, *wks_state, *clip_state;
1491 int i, j, wscount;
1493 make_keys();
1495 if (scr->session_state == NULL)
1496 return;
1498 parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
1500 if (!parr)
1501 return;
1503 wscount = scr->workspace_count;
1504 for (i=0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
1505 wks_state = WMGetFromPLArray(parr, i);
1506 if (WMIsPLDictionary(wks_state))
1507 pstr = WMGetFromPLDictionary(wks_state, dName);
1508 else
1509 pstr = wks_state;
1510 if (i >= scr->workspace_count)
1511 wWorkspaceNew(scr);
1512 if (scr->workspace_menu) {
1513 wfree(scr->workspace_menu->entries[i+2]->text);
1514 scr->workspace_menu->entries[i+2]->text = wstrdup(WMGetFromPLString(pstr));
1515 scr->workspace_menu->flags.realized = 0;
1517 wfree(scr->workspaces[i]->name);
1518 scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
1519 if (!wPreferences.flags.noclip) {
1520 clip_state = WMGetFromPLDictionary(wks_state, dClip);
1521 if (scr->workspaces[i]->clip)
1522 wDockDestroy(scr->workspaces[i]->clip);
1523 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state,
1524 WM_CLIP);
1525 if (i>0)
1526 wDockHideIcons(scr->workspaces[i]->clip);
1528 /* We set the global icons here, because scr->workspaces[i]->clip
1529 * was not valid in wDockRestoreState().
1530 * There we only set icon->omnipresent to know which icons we
1531 * need to set here.
1533 for (j=0; j<scr->workspaces[i]->clip->max_icons; j++) {
1534 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
1536 if (aicon && aicon->omnipresent) {
1537 aicon->omnipresent = 0;
1538 wClipMakeIconOmnipresent(aicon, True);
1539 XMapWindow(dpy, aicon->icon->core->window);
1544 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)i);