xinerama switchpanel corruption fix
[wmaker-crm.git] / src / switchpanel.c
blob07c37f935fad8906196892a74112b3de2548dd97
1 /*
2 * Window Maker window manager
4 * Copyright (c) 1997-2004 Alfredo K. Kojima
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
22 #include "wconfig.h"
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/time.h>
28 #include "WindowMaker.h"
29 #include "screen.h"
30 #include "wcore.h"
31 #include "framewin.h"
32 #include "window.h"
33 #include "defaults.h"
34 #include "switchpanel.h"
35 #include "funcs.h"
36 #include "xinerama.h"
38 #ifdef SHAPE
39 #include <X11/extensions/shape.h>
41 extern Bool wShapeSupported;
42 #endif
45 struct SwitchPanel {
46 WScreen *scr;
47 WMWindow *win;
48 WMFrame *iconBox;
50 WMArray *icons;
51 WMArray *images;
52 WMArray *windows;
53 RImage *bg;
54 int current;
55 int firstVisible;
56 int visibleCount;
58 WMLabel *label;
60 RImage *defIcon;
62 RImage *tileTmp;
63 RImage *tile;
65 WMFont *font;
66 WMColor *white;
73 extern WPreferences wPreferences;
75 #define BORDER_SPACE 10
76 #define ICON_SIZE 48
77 #define ICON_TILE_SIZE 64
78 #define LABEL_HEIGHT 25
79 #define SCREEN_BORDER_SPACING 2*20
80 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
82 static int canReceiveFocus(WWindow *wwin)
84 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
85 return 0;
86 if (!wwin->flags.mapped)
88 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
89 return 0;
90 else
91 return -1;
93 if (WFLAGP(wwin, no_focusable))
94 return 0;
95 return 1;
99 static void changeImage(WSwitchPanel *panel, int idecks, int selected)
101 WMFrame *icon = WMGetFromArray(panel->icons, idecks);
102 RImage *image= WMGetFromArray(panel->images, idecks);
104 if (!panel->bg && !panel->tile) {
105 if (!selected)
106 WMSetFrameRelief(icon, WRFlat);
109 if (image && icon) {
110 RImage *back;
111 int opaq= 255;
112 RImage *tile;
113 WMPoint pos;
114 Pixmap p;
116 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
117 opaq= 50;
119 pos= WMGetViewPosition(WMWidgetView(icon));
120 back= panel->tileTmp;
121 if (panel->bg)
122 RCopyArea(back, panel->bg,
123 BORDER_SPACE+pos.x-panel->firstVisible*ICON_TILE_SIZE, BORDER_SPACE+pos.y,
124 back->width, back->height,
125 0, 0);
126 else
128 RColor color;
129 WMScreen *wscr= WMWidgetScreen(icon);
130 color.red= 255;
131 color.red = WMRedComponentOfColor(WMGrayColor(wscr))>>8;
132 color.green = WMGreenComponentOfColor(WMGrayColor(wscr))>>8;
133 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr))>>8;
134 RFillImage(back, &color);
136 if (selected)
138 tile= panel->tile;
139 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
140 (back->width - tile->width)/2, (back->height - tile->height)/2);
142 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
143 (back->width - image->width)/2, (back->height - image->height)/2,
144 opaq);
146 RConvertImage(panel->scr->rcontext, back, &p);
147 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
148 XClearWindow(dpy, WMWidgetXID(icon));
149 XFreePixmap(dpy, p);
152 if (!panel->bg && !panel->tile) {
153 if (selected)
154 WMSetFrameRelief(icon, WRSimple);
159 static RImage *scaleDownIfNeeded(RImage *image)
161 if (image && ((image->width - ICON_SIZE) > 2 || (image->height - ICON_SIZE) > 2)) {
162 RImage *nimage;
163 nimage= RScaleImage(image, ICON_SIZE, (image->height * ICON_SIZE / image->width));
164 RReleaseImage(image);
165 image= nimage;
167 return image;
171 static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin,
172 int x, int y)
174 WMFrame *icon= WMCreateFrame(parent);
175 RImage *image = NULL;
177 WMSetFrameRelief(icon, WRFlat);
178 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
179 WMMoveWidget(icon, x, y);
181 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
182 image = RRetainImage(wwin->net_icon_image);
184 // Make this use a caching thing. When there are many windows,
185 // it's very likely that most of them are instances of the same thing,
186 // so caching them should get performance acceptable in these cases.
187 if (!image)
188 image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class);
190 if (!image && !panel->defIcon) {
191 char *file = wDefaultGetIconFile(panel->scr, NULL, NULL, False);
192 if (file) {
193 char *path = FindImage(wPreferences.icon_path, file);
194 if (path) {
195 image = RLoadImage(panel->scr->rcontext, path, 0);
196 wfree(path);
199 if (image)
200 panel->defIcon= scaleDownIfNeeded(image);
201 image= NULL;
203 if (!image && panel->defIcon)
204 image= RRetainImage(panel->defIcon);
206 image= scaleDownIfNeeded(image);
208 WMAddToArray(panel->images, image);
209 WMAddToArray(panel->icons, icon);
213 static void scrollIcons(WSwitchPanel *panel, int delta)
215 int nfirst= panel->firstVisible + delta;
216 int i;
217 int count= WMGetArrayItemCount(panel->windows);
218 // int nx, ox;
219 // struct timeval tv1, tv2;
221 if (count <= panel->visibleCount)
222 return;
224 if (nfirst < 0)
225 nfirst= 0;
226 else if (nfirst >= count-panel->visibleCount)
227 nfirst= count-panel->visibleCount;
229 if (nfirst == panel->firstVisible)
230 return;
232 ox = -panel->firstVisible * ICON_TILE_SIZE;
233 nx = -nfirst * ICON_TILE_SIZE;
234 for (i= 0; i < SCROLL_STEPS; i++) {
235 unsigned int diff;
236 gettimeofday(&tv1, NULL);
237 WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
238 XSync(dpy, False);
239 gettimeofday(&tv2, NULL);
240 diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
241 if (diff < 200)
242 wusleep(300-diff);
245 WMMoveWidget(panel->iconBox, -nfirst*ICON_TILE_SIZE, 0);
247 panel->firstVisible= nfirst;
249 for (i= panel->firstVisible; i < panel->firstVisible+panel->visibleCount; i++) {
250 changeImage(panel, i, i == panel->current);
256 * 0 1 2
257 * 3 4 5
258 * 6 7 8
260 static RImage *assemblePuzzleImage(RImage **images, int width, int height)
262 RImage *img= RCreateImage(width, height, 1);
263 RImage *tmp;
264 int tw, th;
265 RColor color;
266 if (!img)
267 return NULL;
269 color.red= 0;
270 color.green= 0;
271 color.blue= 0;
272 color.alpha= 255;
274 RFillImage(img, &color);
276 tw= width-images[0]->width-images[2]->width;
277 th= height-images[0]->height-images[6]->height;
279 if (tw <= 0 || th <= 0) {
280 //XXX
281 return NULL;
284 /* top */
285 if (tw > 0) {
286 tmp= RSmoothScaleImage(images[1], tw, images[1]->height);
287 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
288 images[0]->width, 0);
289 RReleaseImage(tmp);
291 /* bottom */
292 if (tw > 0) {
293 tmp= RSmoothScaleImage(images[7], tw, images[7]->height);
294 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
295 images[6]->width, height-images[6]->height);
296 RReleaseImage(tmp);
298 /* left */
299 if (th > 0) {
300 tmp= RSmoothScaleImage(images[3], images[3]->width, th);
301 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
302 0, images[0]->height);
303 RReleaseImage(tmp);
305 /* right */
306 if (th > 0) {
307 tmp= RSmoothScaleImage(images[5], images[5]->width, th);
308 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
309 width-images[5]->width, images[2]->height);
310 RReleaseImage(tmp);
312 /* center */
313 if (tw > 0 && th > 0) {
314 tmp= RSmoothScaleImage(images[4], tw, th);
315 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
316 images[0]->width, images[0]->height);
317 RReleaseImage(tmp);
320 /* corners */
321 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height,
322 0, 0);
324 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height,
325 width-images[2]->width, 0);
327 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height,
328 0, height-images[6]->height);
330 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
331 width-images[8]->width, height-images[8]->height);
333 return img;
336 static RImage *createBackImage(WScreen *scr, int width, int height)
338 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
342 static RImage *getTile(WSwitchPanel *panel)
344 RImage *stile;
346 if (!wPreferences.swtileImage)
347 return NULL;
349 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
350 if (!stile)
351 return wPreferences.swtileImage;
353 return stile;
357 static void
358 drawTitle(WSwitchPanel *panel, int idecks, char *title)
360 char *ntitle;
361 int width= WMWidgetWidth(panel->win);
362 int x;
364 if (title)
365 ntitle= ShrinkString(panel->font, title, width-2*BORDER_SPACE);
366 else
367 ntitle= NULL;
369 if (panel->bg) {
370 if (ntitle) {
371 if (strcmp(ntitle, title)!=0)
372 x= BORDER_SPACE;
373 else
375 int w= WMWidthOfString(panel->font, ntitle, strlen(ntitle));
377 x= BORDER_SPACE+(idecks-panel->firstVisible)*ICON_TILE_SIZE + ICON_TILE_SIZE/2 - w/2;
378 if (x < BORDER_SPACE)
379 x= BORDER_SPACE;
380 else if (x + w > width-BORDER_SPACE)
381 x= width-BORDER_SPACE-w;
384 XClearWindow(dpy, WMWidgetXID(panel->win));
385 if (ntitle)
386 WMDrawString(panel->scr->wmscreen,
387 WMWidgetXID(panel->win),
388 panel->white, panel->font,
389 x, WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT + WMFontHeight(panel->font)/2,
390 ntitle, strlen(ntitle));
391 } else {
392 if (ntitle)
393 WMSetLabelText(panel->label, ntitle);
395 if (ntitle)
396 free(ntitle);
401 static WMArray *makeWindowListArray(WScreen *scr, WWindow *curwin, int workspace,
402 int include_unmapped)
404 WMArray *windows= WMCreateArray(10);
405 int fl;
406 WWindow *wwin;
408 for (fl= 0; fl < 2; fl++) {
409 for (wwin= curwin; wwin; wwin= wwin->prev) {
410 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
411 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
412 (wwin->flags.mapped || include_unmapped)) {
413 WMAddToArray(windows, wwin);
416 wwin = curwin;
417 /* start over from the beginning of the list */
418 while (wwin->next)
419 wwin = wwin->next;
421 for (wwin= curwin; wwin && wwin != curwin; wwin= wwin->prev) {
422 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
423 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
424 (wwin->flags.mapped || include_unmapped)) {
425 WMAddToArray(windows, wwin);
430 return windows;
437 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, int workspace)
439 WWindow *wwin;
440 WSwitchPanel *panel= wmalloc(sizeof(WSwitchPanel));
441 WMFrame *viewport;
442 int i;
443 int width, height;
444 int iconsThatFitCount;
445 int count;
446 WMRect rect;
447 rect= wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
449 memset(panel, 0, sizeof(WSwitchPanel));
451 panel->scr= scr;
453 panel->windows= makeWindowListArray(scr, curwin, workspace,
454 wPreferences.swtileImage!=0);
455 count= WMGetArrayItemCount(panel->windows);
457 if (count == 0) {
458 WMFreeArray(panel->windows);
459 wfree(panel);
460 return NULL;
463 width= ICON_TILE_SIZE*count;
464 iconsThatFitCount= count;
466 if (width > rect.size.width) {
467 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING)/ICON_TILE_SIZE;
468 width= iconsThatFitCount*ICON_TILE_SIZE;
471 panel->visibleCount= iconsThatFitCount;
473 if (!wPreferences.swtileImage)
474 return panel;
476 height= LABEL_HEIGHT + ICON_TILE_SIZE;
478 panel->tileTmp= RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
479 panel->tile= getTile(panel);
480 if (panel->tile && wPreferences.swbackImage[8]) {
481 panel->bg= createBackImage(scr, width+2*BORDER_SPACE, height+2*BORDER_SPACE);
483 if (!panel->tileTmp || !panel->tile) {
484 if (panel->bg)
485 RReleaseImage(panel->bg);
486 panel->bg= NULL;
487 if (panel->tile)
488 RReleaseImage(panel->tile);
489 panel->tile= NULL;
490 if (panel->tileTmp)
491 RReleaseImage(panel->tileTmp);
492 panel->tileTmp= NULL;
495 panel->white= WMWhiteColor(scr->wmscreen);
496 panel->font= WMBoldSystemFontOfSize(scr->wmscreen, 12);
497 panel->icons= WMCreateArray(count);
498 panel->images= WMCreateArray(count);
500 panel->win = WMCreateWindow(scr->wmscreen, "");
502 if (!panel->bg) {
503 WMFrame *frame = WMCreateFrame(panel->win);
504 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
505 WMSetFrameRelief(frame, WRSimple);
506 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
508 panel->label = WMCreateLabel(panel->win);
509 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
510 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE+ICON_TILE_SIZE+5);
511 WMSetLabelRelief(panel->label, WRSimple);
512 WMSetWidgetBackgroundColor(panel->label, darkGray);
513 WMSetLabelFont(panel->label, panel->font);
514 WMSetLabelTextColor(panel->label, panel->white);
516 WMReleaseColor(darkGray);
517 height+= 5;
520 WMResizeWidget(panel->win, width + 2*BORDER_SPACE, height + 2*BORDER_SPACE);
522 viewport= WMCreateFrame(panel->win);
523 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
524 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
525 WMSetFrameRelief(viewport, WRFlat);
527 panel->iconBox= WMCreateFrame(viewport);
528 WMMoveWidget(panel->iconBox, 0, 0);
529 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE*count, ICON_TILE_SIZE);
530 WMSetFrameRelief(panel->iconBox, WRFlat);
532 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
533 addIconForWindow(panel, panel->iconBox, wwin, i*ICON_TILE_SIZE, 0);
536 WMMapSubwidgets(panel->win);
537 WMRealizeWidget(panel->win);
539 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
540 changeImage(panel, i, 0);
543 if (panel->bg) {
544 Pixmap pixmap, mask;
546 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
548 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
550 #ifdef SHAPE
551 if (mask && wShapeSupported)
552 XShapeCombineMask(dpy, WMWidgetXID(panel->win),
553 ShapeBounding, 0, 0, mask, ShapeSet);
554 #endif
556 if (pixmap)
557 XFreePixmap(dpy, pixmap);
558 if (mask)
559 XFreePixmap(dpy, mask);
563 WMPoint center;
564 center= wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
565 width+2*BORDER_SPACE, height+2*BORDER_SPACE);
566 WMMoveWidget(panel->win, center.x, center.y);
569 panel->current= WMGetFirstInArray(panel->windows, curwin);
570 if (panel->current >= 0)
571 changeImage(panel, panel->current, 1);
573 WMMapWidget(panel->win);
575 return panel;
579 void wSwitchPanelDestroy(WSwitchPanel *panel)
581 int i;
582 RImage *image;
584 if (panel->win)
585 WMUnmapWidget(panel->win);
587 if (panel->images) {
588 WM_ITERATE_ARRAY(panel->images, image, i) {
589 if (image)
590 RReleaseImage(image);
592 WMFreeArray(panel->images);
594 if (panel->win)
595 WMDestroyWidget(panel->win);
596 if (panel->icons)
597 WMFreeArray(panel->icons);
598 WMFreeArray(panel->windows);
599 if (panel->defIcon)
600 RReleaseImage(panel->defIcon);
601 if (panel->tile)
602 RReleaseImage(panel->tile);
603 if (panel->tileTmp)
604 RReleaseImage(panel->tileTmp);
605 if (panel->bg)
606 RReleaseImage(panel->bg);
607 if (panel->font)
608 WMReleaseFont(panel->font);
609 if (panel->white)
610 WMReleaseColor(panel->white);
611 wfree(panel);
615 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back)
617 WWindow *wwin;
618 int count = WMGetArrayItemCount(panel->windows);
620 if (count == 0)
621 return NULL;
623 if (panel->win)
624 changeImage(panel, panel->current, 0);
626 if (back)
627 panel->current--;
628 else
629 panel->current++;
631 wwin = WMGetFromArray(panel->windows, (count+panel->current)%count);
633 if (back)
635 if (panel->current < 0)
636 scrollIcons(panel, count);
637 else if (panel->current < panel->firstVisible)
638 scrollIcons(panel, -1);
640 else
642 if (panel->current >= count)
643 scrollIcons(panel, -count);
644 else if (panel->current - panel->firstVisible >= panel->visibleCount)
645 scrollIcons(panel, 1);
648 panel->current= (count+panel->current)%count;
650 if (panel->win) {
651 drawTitle(panel, panel->current, wwin->frame->title);
653 changeImage(panel, panel->current, 1);
656 return wwin;
660 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
662 WWindow *wwin;
663 int count = WMGetArrayItemCount(panel->windows);
665 if (count == 0)
666 return NULL;
668 if (panel->win)
669 changeImage(panel, panel->current, 0);
671 if (back) {
672 panel->current = count-1;
673 scrollIcons(panel, count);
674 } else {
675 panel->current = 0;
676 scrollIcons(panel, -count);
679 wwin = WMGetFromArray(panel->windows, panel->current);
681 if (panel->win) {
682 drawTitle(panel, panel->current, wwin->frame->title);
684 changeImage(panel, panel->current, 1);
686 return wwin;
690 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
692 WMFrame *icon;
693 int i;
694 int focus= -1;
696 if (!panel->win)
697 return NULL;
699 /* if (event->type == LeaveNotify) {
700 if (event->xcrossing.window == WMWidgetXID(panel->win))
701 focus= 0;
702 } else*/ if (event->type == MotionNotify) {
704 WM_ITERATE_ARRAY(panel->icons, icon, i) {
705 if (WMWidgetXID(icon) == event->xmotion.window) {
706 focus= i;
707 break;
711 if (focus >= 0 && panel->current != focus) {
712 WWindow *wwin;
714 changeImage(panel, panel->current, 0);
715 changeImage(panel, focus, 1);
716 panel->current= focus;
718 wwin= WMGetFromArray(panel->windows, focus);
720 drawTitle(panel, panel->current, wwin->frame->title);
722 return wwin;
725 return NULL;
729 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
731 if (!swpanel->win)
732 return None;
733 return WMWidgetXID(swpanel->win);