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,
28 #include "WindowMaker.h"
34 #include "switchpanel.h"
39 extern Atom _XA_WM_IGNORE_FOCUS_EVENTS
;
42 #include <X11/extensions/shape.h>
44 extern Bool wShapeSupported
;
71 extern WPreferences wPreferences
;
73 #define BORDER_SPACE 10
75 #define ICON_TILE_SIZE 64
76 #define LABEL_HEIGHT 25
77 #define SCREEN_BORDER_SPACING 2*20
78 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
80 static int canReceiveFocus(WWindow
* wwin
)
82 if (wwin
->frame
->workspace
!= wwin
->screen_ptr
->current_workspace
)
84 if (!wwin
->flags
.mapped
) {
85 if (!wwin
->flags
.shaded
&& !wwin
->flags
.miniaturized
&& !wwin
->flags
.hidden
)
90 if (WFLAGP(wwin
, no_focusable
))
95 static void changeImage(WSwitchPanel
* panel
, int idecks
, int selected
)
97 WMFrame
*icon
= WMGetFromArray(panel
->icons
, idecks
);
98 RImage
*image
= WMGetFromArray(panel
->images
, idecks
);
100 if (!panel
->bg
&& !panel
->tile
) {
102 WMSetFrameRelief(icon
, WRFlat
);
112 if (canReceiveFocus(WMGetFromArray(panel
->windows
, idecks
)) < 0)
115 pos
= WMGetViewPosition(WMWidgetView(icon
));
116 back
= panel
->tileTmp
;
118 RCopyArea(back
, panel
->bg
,
119 BORDER_SPACE
+ pos
.x
- panel
->firstVisible
* ICON_TILE_SIZE
,
120 BORDER_SPACE
+ pos
.y
, back
->width
, back
->height
, 0, 0);
123 WMScreen
*wscr
= WMWidgetScreen(icon
);
125 color
.red
= WMRedComponentOfColor(WMGrayColor(wscr
)) >> 8;
126 color
.green
= WMGreenComponentOfColor(WMGrayColor(wscr
)) >> 8;
127 color
.blue
= WMBlueComponentOfColor(WMGrayColor(wscr
)) >> 8;
128 RFillImage(back
, &color
);
132 RCombineArea(back
, tile
, 0, 0, tile
->width
, tile
->height
,
133 (back
->width
- tile
->width
) / 2, (back
->height
- tile
->height
) / 2);
135 RCombineAreaWithOpaqueness(back
, image
, 0, 0, image
->width
, image
->height
,
136 (back
->width
- image
->width
) / 2, (back
->height
- image
->height
) / 2,
139 RConvertImage(panel
->scr
->rcontext
, back
, &p
);
140 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(icon
), p
);
141 XClearWindow(dpy
, WMWidgetXID(icon
));
145 if (!panel
->bg
&& !panel
->tile
) {
147 WMSetFrameRelief(icon
, WRSimple
);
151 static RImage
*scaleDownIfNeeded(RImage
* image
)
153 if (image
&& ((image
->width
- ICON_SIZE
) > 2 || (image
->height
- ICON_SIZE
) > 2)) {
155 nimage
= RScaleImage(image
, ICON_SIZE
, (image
->height
* ICON_SIZE
/ image
->width
));
156 RReleaseImage(image
);
162 static void addIconForWindow(WSwitchPanel
* panel
, WMWidget
* parent
, WWindow
* wwin
, int x
, int y
)
164 WMFrame
*icon
= WMCreateFrame(parent
);
165 RImage
*image
= NULL
;
167 WMSetFrameRelief(icon
, WRFlat
);
168 WMResizeWidget(icon
, ICON_TILE_SIZE
, ICON_TILE_SIZE
);
169 WMMoveWidget(icon
, x
, y
);
171 if (!WFLAGP(wwin
, always_user_icon
) && wwin
->net_icon_image
)
172 image
= RRetainImage(wwin
->net_icon_image
);
174 // Make this use a caching thing. When there are many windows,
175 // it's very likely that most of them are instances of the same thing,
176 // so caching them should get performance acceptable in these cases.
178 image
= wDefaultGetImage(panel
->scr
, wwin
->wm_instance
, wwin
->wm_class
);
180 if (!image
&& !panel
->defIcon
) {
181 char *file
= wDefaultGetIconFile(panel
->scr
, NULL
, NULL
, False
);
183 char *path
= FindImage(wPreferences
.icon_path
, file
);
185 image
= RLoadImage(panel
->scr
->rcontext
, path
, 0);
190 panel
->defIcon
= scaleDownIfNeeded(image
);
193 if (!image
&& panel
->defIcon
)
194 image
= RRetainImage(panel
->defIcon
);
196 image
= scaleDownIfNeeded(image
);
198 WMAddToArray(panel
->images
, image
);
199 WMAddToArray(panel
->icons
, icon
);
202 static void scrollIcons(WSwitchPanel
* panel
, int delta
)
204 int nfirst
= panel
->firstVisible
+ delta
;
206 int count
= WMGetArrayItemCount(panel
->windows
);
208 // struct timeval tv1, tv2;
210 if (count
<= panel
->visibleCount
)
215 else if (nfirst
>= count
- panel
->visibleCount
)
216 nfirst
= count
- panel
->visibleCount
;
218 if (nfirst
== panel
->firstVisible
)
221 ox = -panel->firstVisible * ICON_TILE_SIZE;
222 nx = -nfirst * ICON_TILE_SIZE;
223 for (i= 0; i < SCROLL_STEPS; i++) {
225 gettimeofday(&tv1, NULL);
226 WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
228 gettimeofday(&tv2, NULL);
229 diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
234 WMMoveWidget(panel
->iconBox
, -nfirst
* ICON_TILE_SIZE
, 0);
236 panel
->firstVisible
= nfirst
;
238 for (i
= panel
->firstVisible
; i
< panel
->firstVisible
+ panel
->visibleCount
; i
++) {
239 changeImage(panel
, i
, i
== panel
->current
);
248 static RImage
*assemblePuzzleImage(RImage
** images
, int width
, int height
)
250 RImage
*img
= RCreateImage(width
, height
, 1);
262 RFillImage(img
, &color
);
264 tw
= width
- images
[0]->width
- images
[2]->width
;
265 th
= height
- images
[0]->height
- images
[6]->height
;
267 if (tw
<= 0 || th
<= 0) {
274 tmp
= RSmoothScaleImage(images
[1], tw
, images
[1]->height
);
275 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, images
[0]->width
, 0);
280 tmp
= RSmoothScaleImage(images
[7], tw
, images
[7]->height
);
281 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, images
[6]->width
, height
- images
[6]->height
);
286 tmp
= RSmoothScaleImage(images
[3], images
[3]->width
, th
);
287 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, 0, images
[0]->height
);
292 tmp
= RSmoothScaleImage(images
[5], images
[5]->width
, th
);
293 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, width
- images
[5]->width
, images
[2]->height
);
297 if (tw
> 0 && th
> 0) {
298 tmp
= RSmoothScaleImage(images
[4], tw
, th
);
299 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, images
[0]->width
, images
[0]->height
);
304 RCopyArea(img
, images
[0], 0, 0, images
[0]->width
, images
[0]->height
, 0, 0);
306 RCopyArea(img
, images
[2], 0, 0, images
[2]->width
, images
[2]->height
, width
- images
[2]->width
, 0);
308 RCopyArea(img
, images
[6], 0, 0, images
[6]->width
, images
[6]->height
, 0, height
- images
[6]->height
);
310 RCopyArea(img
, images
[8], 0, 0, images
[8]->width
, images
[8]->height
,
311 width
- images
[8]->width
, height
- images
[8]->height
);
316 static RImage
*createBackImage(WScreen
* scr
, int width
, int height
)
318 return assemblePuzzleImage(wPreferences
.swbackImage
, width
, height
);
321 static RImage
*getTile(WSwitchPanel
* panel
)
325 if (!wPreferences
.swtileImage
)
328 stile
= RScaleImage(wPreferences
.swtileImage
, ICON_TILE_SIZE
, ICON_TILE_SIZE
);
330 return wPreferences
.swtileImage
;
335 static void drawTitle(WSwitchPanel
* panel
, int idecks
, char *title
)
338 int width
= WMWidgetWidth(panel
->win
);
342 ntitle
= ShrinkString(panel
->font
, title
, width
- 2 * BORDER_SPACE
);
348 if (strcmp(ntitle
, title
) != 0)
351 int w
= WMWidthOfString(panel
->font
, ntitle
, strlen(ntitle
));
353 x
= BORDER_SPACE
+ (idecks
- panel
->firstVisible
) * ICON_TILE_SIZE
+
354 ICON_TILE_SIZE
/ 2 - w
/ 2;
355 if (x
< BORDER_SPACE
)
357 else if (x
+ w
> width
- BORDER_SPACE
)
358 x
= width
- BORDER_SPACE
- w
;
361 XClearWindow(dpy
, WMWidgetXID(panel
->win
));
363 WMDrawString(panel
->scr
->wmscreen
,
364 WMWidgetXID(panel
->win
),
365 panel
->white
, panel
->font
,
367 WMWidgetHeight(panel
->win
) - BORDER_SPACE
- LABEL_HEIGHT
+
368 WMFontHeight(panel
->font
) / 2, ntitle
, strlen(ntitle
));
371 WMSetLabelText(panel
->label
, ntitle
);
377 static WMArray
*makeWindowListArray(WScreen
* scr
, WWindow
* curwin
, int workspace
, int include_unmapped
, Bool class_only
)
379 WMArray
*windows
= WMCreateArray(10);
383 for (fl
= 0; fl
< 2; fl
++) {
384 for (wwin
= curwin
; wwin
; wwin
= wwin
->prev
) {
385 if (((!fl
&& canReceiveFocus(wwin
) > 0) || (fl
&& canReceiveFocus(wwin
) < 0)) &&
386 (!WFLAGP(wwin
, skip_window_list
) || wwin
->flags
.internal_window
) &&
387 (wwin
->flags
.mapped
|| include_unmapped
)) {
389 if (!wwin
->wm_class
|| !curwin
->wm_class
)
391 if (strcmp(wwin
->wm_class
, curwin
->wm_class
))
394 WMAddToArray(windows
, wwin
);
398 /* start over from the beginning of the list */
402 for (wwin
= curwin
; wwin
&& wwin
!= curwin
; wwin
= wwin
->prev
) {
403 if (((!fl
&& canReceiveFocus(wwin
) > 0) || (fl
&& canReceiveFocus(wwin
) < 0)) &&
404 (!WFLAGP(wwin
, skip_window_list
) || wwin
->flags
.internal_window
) &&
405 (wwin
->flags
.mapped
|| include_unmapped
)) {
407 if (!wwin
->wm_class
|| !curwin
->wm_class
)
409 if (strcmp(wwin
->wm_class
, curwin
->wm_class
))
412 WMAddToArray(windows
, wwin
);
420 WSwitchPanel
*wInitSwitchPanel(WScreen
* scr
, WWindow
* curwin
, int workspace
, Bool class_only
)
423 WSwitchPanel
*panel
= wmalloc(sizeof(WSwitchPanel
));
427 int iconsThatFitCount
;
430 rect
= wGetRectForHead(scr
, wGetHeadForPointerLocation(scr
));
432 memset(panel
, 0, sizeof(WSwitchPanel
));
436 panel
->windows
= makeWindowListArray(scr
, curwin
, workspace
, wPreferences
.swtileImage
!= 0, class_only
);
437 count
= WMGetArrayItemCount(panel
->windows
);
440 WMFreeArray(panel
->windows
);
445 width
= ICON_TILE_SIZE
* count
;
446 iconsThatFitCount
= count
;
448 if (width
> rect
.size
.width
) {
449 iconsThatFitCount
= (rect
.size
.width
- SCREEN_BORDER_SPACING
) / ICON_TILE_SIZE
;
450 width
= iconsThatFitCount
* ICON_TILE_SIZE
;
453 panel
->visibleCount
= iconsThatFitCount
;
455 if (!wPreferences
.swtileImage
)
458 height
= LABEL_HEIGHT
+ ICON_TILE_SIZE
;
460 panel
->tileTmp
= RCreateImage(ICON_TILE_SIZE
, ICON_TILE_SIZE
, 1);
461 panel
->tile
= getTile(panel
);
462 if (panel
->tile
&& wPreferences
.swbackImage
[8]) {
463 panel
->bg
= createBackImage(scr
, width
+ 2 * BORDER_SPACE
, height
+ 2 * BORDER_SPACE
);
465 if (!panel
->tileTmp
|| !panel
->tile
) {
467 RReleaseImage(panel
->bg
);
470 RReleaseImage(panel
->tile
);
473 RReleaseImage(panel
->tileTmp
);
474 panel
->tileTmp
= NULL
;
477 panel
->white
= WMWhiteColor(scr
->wmscreen
);
478 panel
->font
= WMBoldSystemFontOfSize(scr
->wmscreen
, 12);
479 panel
->icons
= WMCreateArray(count
);
480 panel
->images
= WMCreateArray(count
);
482 panel
->win
= WMCreateWindow(scr
->wmscreen
, "");
485 WMFrame
*frame
= WMCreateFrame(panel
->win
);
486 WMColor
*darkGray
= WMDarkGrayColor(scr
->wmscreen
);
487 WMSetFrameRelief(frame
, WRSimple
);
488 WMSetViewExpandsToParent(WMWidgetView(frame
), 0, 0, 0, 0);
490 panel
->label
= WMCreateLabel(panel
->win
);
491 WMResizeWidget(panel
->label
, width
, LABEL_HEIGHT
);
492 WMMoveWidget(panel
->label
, BORDER_SPACE
, BORDER_SPACE
+ ICON_TILE_SIZE
+ 5);
493 WMSetLabelRelief(panel
->label
, WRSimple
);
494 WMSetWidgetBackgroundColor(panel
->label
, darkGray
);
495 WMSetLabelFont(panel
->label
, panel
->font
);
496 WMSetLabelTextColor(panel
->label
, panel
->white
);
498 WMReleaseColor(darkGray
);
502 WMResizeWidget(panel
->win
, width
+ 2 * BORDER_SPACE
, height
+ 2 * BORDER_SPACE
);
504 viewport
= WMCreateFrame(panel
->win
);
505 WMResizeWidget(viewport
, width
, ICON_TILE_SIZE
);
506 WMMoveWidget(viewport
, BORDER_SPACE
, BORDER_SPACE
);
507 WMSetFrameRelief(viewport
, WRFlat
);
509 panel
->iconBox
= WMCreateFrame(viewport
);
510 WMMoveWidget(panel
->iconBox
, 0, 0);
511 WMResizeWidget(panel
->iconBox
, ICON_TILE_SIZE
* count
, ICON_TILE_SIZE
);
512 WMSetFrameRelief(panel
->iconBox
, WRFlat
);
514 WM_ITERATE_ARRAY(panel
->windows
, wwin
, i
) {
515 addIconForWindow(panel
, panel
->iconBox
, wwin
, i
* ICON_TILE_SIZE
, 0);
518 WMMapSubwidgets(panel
->win
);
519 WMRealizeWidget(panel
->win
);
521 WM_ITERATE_ARRAY(panel
->windows
, wwin
, i
) {
522 changeImage(panel
, i
, 0);
528 RConvertImageMask(scr
->rcontext
, panel
->bg
, &pixmap
, &mask
, 250);
530 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(panel
->win
), pixmap
);
533 if (mask
&& wShapeSupported
)
534 XShapeCombineMask(dpy
, WMWidgetXID(panel
->win
), ShapeBounding
, 0, 0, mask
, ShapeSet
);
538 XFreePixmap(dpy
, pixmap
);
540 XFreePixmap(dpy
, mask
);
545 center
= wGetPointToCenterRectInHead(scr
, wGetHeadForPointerLocation(scr
),
546 width
+ 2 * BORDER_SPACE
, height
+ 2 * BORDER_SPACE
);
547 WMMoveWidget(panel
->win
, center
.x
, center
.y
);
550 panel
->current
= WMGetFirstInArray(panel
->windows
, curwin
);
551 if (panel
->current
>= 0)
552 changeImage(panel
, panel
->current
, 1);
554 WMMapWidget(panel
->win
);
559 void wSwitchPanelDestroy(WSwitchPanel
* panel
)
565 Window info_win
= panel
->scr
->info_window
;
567 ev
.xclient
.type
= ClientMessage
;
568 ev
.xclient
.message_type
= _XA_WM_IGNORE_FOCUS_EVENTS
;
569 ev
.xclient
.format
= 32;
570 ev
.xclient
.data
.l
[0] = True
;
572 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
573 WMUnmapWidget(panel
->win
);
575 ev
.xclient
.data
.l
[0] = False
;
576 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
580 WM_ITERATE_ARRAY(panel
->images
, image
, i
) {
582 RReleaseImage(image
);
584 WMFreeArray(panel
->images
);
587 WMDestroyWidget(panel
->win
);
589 WMFreeArray(panel
->icons
);
590 WMFreeArray(panel
->windows
);
592 RReleaseImage(panel
->defIcon
);
594 RReleaseImage(panel
->tile
);
596 RReleaseImage(panel
->tileTmp
);
598 RReleaseImage(panel
->bg
);
600 WMReleaseFont(panel
->font
);
602 WMReleaseColor(panel
->white
);
606 WWindow
*wSwitchPanelSelectNext(WSwitchPanel
* panel
, int back
)
609 int count
= WMGetArrayItemCount(panel
->windows
);
615 changeImage(panel
, panel
->current
, 0);
622 wwin
= WMGetFromArray(panel
->windows
, (count
+ panel
->current
) % count
);
625 if (panel
->current
< 0)
626 scrollIcons(panel
, count
);
627 else if (panel
->current
< panel
->firstVisible
)
628 scrollIcons(panel
, -1);
630 if (panel
->current
>= count
)
631 scrollIcons(panel
, -count
);
632 else if (panel
->current
- panel
->firstVisible
>= panel
->visibleCount
)
633 scrollIcons(panel
, 1);
636 panel
->current
= (count
+ panel
->current
) % count
;
639 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
641 changeImage(panel
, panel
->current
, 1);
647 WWindow
*wSwitchPanelSelectFirst(WSwitchPanel
* panel
, int back
)
650 int count
= WMGetArrayItemCount(panel
->windows
);
656 changeImage(panel
, panel
->current
, 0);
659 panel
->current
= count
- 1;
660 scrollIcons(panel
, count
);
663 scrollIcons(panel
, -count
);
666 wwin
= WMGetFromArray(panel
->windows
, panel
->current
);
669 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
671 changeImage(panel
, panel
->current
, 1);
676 WWindow
*wSwitchPanelHandleEvent(WSwitchPanel
* panel
, XEvent
* event
)
685 /* if (event->type == LeaveNotify) {
686 if (event->xcrossing.window == WMWidgetXID(panel->win))
688 } else */ if (event
->type
== MotionNotify
) {
690 WM_ITERATE_ARRAY(panel
->icons
, icon
, i
) {
691 if (WMWidgetXID(icon
) == event
->xmotion
.window
) {
697 if (focus
>= 0 && panel
->current
!= focus
) {
700 changeImage(panel
, panel
->current
, 0);
701 changeImage(panel
, focus
, 1);
702 panel
->current
= focus
;
704 wwin
= WMGetFromArray(panel
->windows
, focus
);
706 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
714 Window
wSwitchPanelGetWindow(WSwitchPanel
* swpanel
)
718 return WMWidgetXID(swpanel
->win
);