1 /* wsmap.c - worskpace map
3 * Window Maker window manager
5 * Copyright (c) 2014 Window Maker Team - David Maciejak
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.
28 #include <X11/extensions/shape.h>
34 #include "workspace.h"
37 #include "WINGs/WINGsP.h"
40 static const int WORKSPACE_MAP_RATIO
= 10;
41 static const int WORKSPACE_SEPARATOR_WIDTH
= 12;
42 static const int mini_workspace_per_line
= 5;
45 * Used to store the index of the tenth displayed mini workspace
46 * will be 0 for workspaces number 0 to 9
47 * 1 for workspaces number 10 -> 19
49 static int wsmap_bulk_index
;
50 static WMPixmap
*frame_bg_focused
;
51 static WMPixmap
*frame_bg_unfocused
;
57 int wswidth
, wsheight
;
58 int mini_workspace_width
, mini_workspace_height
;
64 WMButton
*workspace_img_button
;
65 WMLabel
*workspace_label
;
68 void wWorkspaceMapUpdate(WScreen
*scr
)
72 pimg
= XGetImage(dpy
, scr
->root_win
, 0, 0,
73 scr
->scr_width
, scr
->scr_height
,
78 apercu
= RCreateImageFromXImage(scr
->rcontext
, pimg
, NULL
);
82 RImage
*tmp
= scr
->workspaces
[scr
->current_workspace
]->map
;
87 scr
->workspaces
[scr
->current_workspace
]->map
=
88 RSmoothScaleImage(apercu
,
89 scr
->scr_width
/ WORKSPACE_MAP_RATIO
,
90 scr
->scr_height
/ WORKSPACE_MAP_RATIO
);
91 RReleaseImage(apercu
);
96 static void workspace_map_slide(WWorkspaceMap
*wsmap
)
98 if (wsmap
->edge
== WD_TOP
)
99 SlideWindow(WMWidgetXID(wsmap
->win
), 0, -1 * wsmap
->wsheight
, wsmap
->xcount
, wsmap
->ycount
);
101 SlideWindow(WMWidgetXID(wsmap
->win
), 0, wsmap
->scr
->scr_height
, wsmap
->xcount
, wsmap
->ycount
);
104 static void workspace_map_unslide(WWorkspaceMap
*wsmap
)
106 if (wsmap
->edge
== WD_TOP
)
107 SlideWindow(WMWidgetXID(wsmap
->win
), wsmap
->xcount
, wsmap
->ycount
, 0, -1 * wsmap
->wsheight
);
109 SlideWindow(WMWidgetXID(wsmap
->win
), wsmap
->xcount
, wsmap
->ycount
, 0, wsmap
->scr
->scr_height
);
112 static void workspace_map_destroy(WWorkspaceMap
*wsmap
)
114 workspace_map_unslide(wsmap
);
115 WMUnmapWidget(wsmap
->win
);
118 Window info_win
= wsmap
->scr
->info_window
;
121 ev
.xclient
.type
= ClientMessage
;
122 ev
.xclient
.message_type
= w_global
.atom
.wm
.ignore_focus_events
;
123 ev
.xclient
.format
= 32;
124 ev
.xclient
.data
.l
[0] = True
;
126 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
127 WMUnmapWidget(wsmap
->win
);
129 ev
.xclient
.data
.l
[0] = False
;
130 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
131 WMDestroyWidget(wsmap
->win
);
133 if (frame_bg_focused
)
134 WMReleasePixmap(frame_bg_focused
);
135 if (frame_bg_unfocused
)
136 WMReleasePixmap(frame_bg_unfocused
);
141 static void selected_workspace_callback(WMWidget
*w
, void *data
)
143 WWorkspaceMap
*wsmap
= (WWorkspaceMap
*) data
;
144 WMButton
*click_button
= w
;
147 int workspace_id
= atoi(WMGetButtonText(click_button
));
149 wWorkspaceChange(wsmap
->scr
, workspace_id
);
150 w_global
.process_workspacemap_event
= False
;
154 static void set_workspace_map_background_image(WWorkspaceMap
*wsmap
)
158 if (wPreferences
.wsmbackTexture
->any
.type
== WTEX_PIXMAP
) {
159 RImage
*tmp
= wTextureRenderImage(wPreferences
.wsmbackTexture
, wsmap
->wswidth
, wsmap
->wsheight
, WREL_FLAT
);
164 RConvertImageMask(wsmap
->scr
->rcontext
, tmp
, &pixmap
, &mask
, 250);
170 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(wsmap
->win
), pixmap
);
173 if (mask
&& w_global
.xext
.shape
.supported
)
174 XShapeCombineMask(dpy
, WMWidgetXID(wsmap
->win
), ShapeBounding
, 0, 0, mask
, ShapeSet
);
178 XFreePixmap(dpy
, pixmap
);
180 XFreePixmap(dpy
, mask
);
184 static void workspace_map_show(WWorkspaceMap
*wsmap
)
186 WMMapSubwidgets(wsmap
->win
);
187 WMMapWidget(wsmap
->win
);
188 workspace_map_slide(wsmap
);
191 static WMPixmap
*get_frame_background_color(WWorkspaceMap
*wsmap
, unsigned int width
, unsigned int height
, int type
)
196 if (!wsmap
->scr
->window_title_texture
[type
])
199 img
= wTextureRenderImage(wsmap
->scr
->window_title_texture
[type
], width
, height
, WREL_FLAT
);
203 pix
= WMCreatePixmapFromRImage(wsmap
->scr
->wmscreen
, img
, 128);
209 static void workspace_map_realize(WWorkspaceMap
*wsmap
, WMFrame
*frame_border
, W_WorkspaceMap
*wsmap_array
)
211 int i
, mini_workspace_cnt
, general_index
;
212 WMPixmap
*frame_border_pixmap
;
215 WMRealizeWidget(wsmap
->win
);
216 set_workspace_map_background_image(wsmap
);
218 frame_border_pixmap
= get_frame_background_color(wsmap
, wsmap
->wswidth
, wsmap
->border_width
, WS_FOCUSED
);
219 WMSetWidgetBackgroundPixmap(frame_border
, frame_border_pixmap
);
220 WMReleasePixmap(frame_border_pixmap
);
222 label_size
= WMGetViewSize(W_VIEW(wsmap_array
[0].workspace_label
));
223 frame_bg_focused
= get_frame_background_color(wsmap
, label_size
.width
, label_size
.height
, WS_FOCUSED
);
224 frame_bg_unfocused
= get_frame_background_color(wsmap
, label_size
.width
, label_size
.height
, WS_UNFOCUSED
);
226 mini_workspace_cnt
= (wsmap
->scr
->workspace_count
<= 2 * mini_workspace_per_line
) ? wsmap
->scr
->workspace_count
: 2 * mini_workspace_per_line
;
227 for (i
= 0; i
< mini_workspace_cnt
; i
++) {
228 general_index
= i
+ wsmap_bulk_index
* 2 * mini_workspace_per_line
;
229 if (general_index
== wsmap
->scr
->current_workspace
) {
230 WMSetWidgetBackgroundPixmap(wsmap_array
[i
].workspace_label
, frame_bg_focused
);
231 WMSetLabelTextColor(wsmap_array
[i
].workspace_label
, wsmap
->scr
->window_title_color
[WS_FOCUSED
]);
233 WMSetWidgetBackgroundPixmap(wsmap_array
[i
].workspace_label
, frame_bg_unfocused
);
234 WMSetLabelTextColor(wsmap_array
[i
].workspace_label
, wsmap
->scr
->window_title_color
[WS_UNFOCUSED
]);
239 static WMPixmap
*enlight_workspace(WScreen
*scr
, RImage
*mini_wkspace_map
)
241 RImage
*tmp
= RCloneImage(mini_wkspace_map
);
245 color
.red
= color
.green
= color
.blue
= 0;
247 RLightImage(tmp
, &color
);
248 icon
= WMCreatePixmapFromRImage(scr
->wmscreen
, tmp
, 128);
254 static WMPixmap
*dummy_background_pixmap(WWorkspaceMap
*wsmap
)
259 img
= RCreateImage(wsmap
->wswidth
, wsmap
->wsheight
, 0);
264 /* the workspace texture is not saved anywhere, so just using the default unfocus color */
265 if (wsmap
->scr
->window_title_texture
[WS_UNFOCUSED
]) {
266 RColor frame_bg_color
;
268 frame_bg_color
.red
= wsmap
->scr
->window_title_texture
[WS_UNFOCUSED
]->solid
.normal
.red
;
269 frame_bg_color
.green
= wsmap
->scr
->window_title_texture
[WS_UNFOCUSED
]->solid
.normal
.green
;
270 frame_bg_color
.blue
= wsmap
->scr
->window_title_texture
[WS_UNFOCUSED
]->solid
.normal
.blue
;
271 RFillImage(img
, &frame_bg_color
);
274 icon
= WMCreatePixmapFromRImage(wsmap
->scr
->wmscreen
, img
, 128);
280 static void show_mini_workspace(WWorkspaceMap
*wsmap
, W_WorkspaceMap
*wsmap_array
, int max_mini_workspace
)
282 int index
, space_width
;
283 int border_width_adjustement
= (wsmap
->edge
== WD_TOP
) ? 0 : wsmap
->border_width
;
284 int font_height
= WMFontHeight(wsmap
->scr
->info_text_font
);
286 if (max_mini_workspace
> mini_workspace_per_line
)
287 space_width
= (wsmap
->wswidth
- mini_workspace_per_line
* wsmap
->mini_workspace_width
) / (mini_workspace_per_line
+ 1);
289 space_width
= (wsmap
->wswidth
- max_mini_workspace
* wsmap
->mini_workspace_width
) / (max_mini_workspace
+ 1);
291 for (index
= 0; index
< max_mini_workspace
; index
++) {
296 if (index
>= mini_workspace_per_line
) {
298 j
-= mini_workspace_per_line
;
302 if (wsmap_array
[index
].workspace_img_button
) {
303 WMResizeWidget(wsmap_array
[index
].workspace_img_button
, wsmap
->mini_workspace_width
, wsmap
->mini_workspace_height
);
304 WMMoveWidget(wsmap_array
[index
].workspace_img_button
, j
* wsmap
->mini_workspace_width
+ (j
+ 1) * space_width
,
305 border_width_adjustement
+ WORKSPACE_SEPARATOR_WIDTH
+
306 i
* (wsmap
->mini_workspace_height
+ 2 * WORKSPACE_SEPARATOR_WIDTH
) + font_height
);
307 WMMapWidget(wsmap_array
[index
].workspace_img_button
);
309 if (wsmap_array
[index
].workspace_label
) {
310 WMResizeWidget(wsmap_array
[index
].workspace_label
, wsmap
->mini_workspace_width
, font_height
);
311 WMMoveWidget(wsmap_array
[index
].workspace_label
, j
* wsmap
->mini_workspace_width
+ (j
+ 1) * space_width
,
312 border_width_adjustement
+ WORKSPACE_SEPARATOR_WIDTH
+
313 i
* (wsmap
->mini_workspace_height
+ 2 * WORKSPACE_SEPARATOR_WIDTH
));
314 WMMapWidget(wsmap_array
[index
].workspace_label
);
319 static void hide_mini_workspace(W_WorkspaceMap
*wsmap_array
, int i
)
321 if (wsmap_array
[i
].workspace_img_button
)
322 WMUnmapWidget(wsmap_array
[i
].workspace_img_button
);
323 if (wsmap_array
[i
].workspace_label
)
324 WMUnmapWidget(wsmap_array
[i
].workspace_label
);
327 static WMPixmap
*get_mini_workspace(WWorkspaceMap
*wsmap
, int index
)
329 if (!wsmap
->scr
->workspaces
[index
]->map
)
330 return dummy_background_pixmap(wsmap
);
332 if (index
== wsmap
->scr
->current_workspace
)
333 return enlight_workspace(wsmap
->scr
, wsmap
->scr
->workspaces
[index
]->map
);
335 return WMCreatePixmapFromRImage(wsmap
->scr
->wmscreen
, wsmap
->scr
->workspaces
[index
]->map
, 128);
338 static void create_mini_workspace(WScreen
*scr
, WWorkspaceMap
*wsmap
, W_WorkspaceMap
*wsmap_array
)
341 int mini_workspace_cnt
;
343 WMButton
*mini_workspace_btn
;
346 /* by default display the 10 first mini workspaces */
347 wsmap_bulk_index
= 0;
348 mini_workspace_cnt
= (scr
->workspace_count
<= 2 * mini_workspace_per_line
) ? scr
->workspace_count
: 2 * mini_workspace_per_line
;
349 for (workspace_index
= 0; workspace_index
< mini_workspace_cnt
; workspace_index
++) {
350 mini_workspace_btn
= WMCreateButton(wsmap
->win
, WBTOnOff
);
351 WMSetButtonBordered(mini_workspace_btn
, 0);
352 WMLabel
*workspace_name_label
= WMCreateLabel(wsmap
->win
);
353 WMSetLabelFont(workspace_name_label
, scr
->info_text_font
);
354 WMSetLabelText(workspace_name_label
, scr
->workspaces
[workspace_index
]->name
);
356 wsmap_array
[workspace_index
].workspace_img_button
= mini_workspace_btn
;
357 wsmap_array
[workspace_index
].workspace_label
= workspace_name_label
;
359 WMSetButtonImagePosition(mini_workspace_btn
, WIPImageOnly
);
360 icon
= get_mini_workspace(wsmap
, workspace_index
);
362 WMSetButtonImage(mini_workspace_btn
, icon
);
363 WMReleasePixmap(icon
);
366 snprintf(name
, sizeof(name
), "%d", workspace_index
);
367 WMSetButtonText(mini_workspace_btn
, name
);
368 WMSetButtonAction(mini_workspace_btn
, selected_workspace_callback
, wsmap
);
370 show_mini_workspace(wsmap
, wsmap_array
, mini_workspace_cnt
);
373 static WWorkspaceMap
*create_workspace_map(WScreen
*scr
, W_WorkspaceMap
*wsmap_array
, int edge
)
375 WWorkspaceMap
*wsmap
= wmalloc(sizeof(WWorkspaceMap
));
377 wsmap
->border_width
= 5;
379 wsmap
->mini_workspace_width
= scr
->scr_width
/ WORKSPACE_MAP_RATIO
;
380 wsmap
->mini_workspace_height
= scr
->scr_height
/ WORKSPACE_MAP_RATIO
;
382 if (scr
->workspace_count
== 0)
386 wsmap
->win
= WMCreateWindow(scr
->wmscreen
, "wsmap");
387 wsmap
->wswidth
= WidthOfScreen(DefaultScreenOfDisplay(dpy
));
388 wsmap
->wsheight
= WMFontHeight(scr
->info_text_font
) + (wsmap
->mini_workspace_height
+ 2 * WORKSPACE_SEPARATOR_WIDTH
) *
389 (scr
->workspace_count
> mini_workspace_per_line
? 2 : 1);
391 if (wPreferences
.wsmbackTexture
->any
.type
== WTEX_SOLID
) {
392 WMColor
*tmp
= WMCreateRGBColor(scr
->wmscreen
,
393 wPreferences
.wsmbackTexture
->any
.color
.red
,
394 wPreferences
.wsmbackTexture
->any
.color
.green
,
395 wPreferences
.wsmbackTexture
->any
.color
.blue
,
397 WMSetWidgetBackgroundColor(wsmap
->win
, tmp
);
400 WMResizeWidget(wsmap
->win
, wsmap
->wswidth
, wsmap
->wsheight
+ wsmap
->border_width
);
402 WMFrame
*framel
= WMCreateFrame(wsmap
->win
);
403 WMResizeWidget(framel
, wsmap
->wswidth
, wsmap
->border_width
);
404 WMSetFrameRelief(framel
, WRSimple
);
405 wWorkspaceMapUpdate(scr
);
408 if (edge
== WD_TOP
) {
410 WMMoveWidget(framel
, 0, wsmap
->wsheight
);
412 wsmap
->ycount
= scr
->scr_height
- wsmap
->wsheight
- wsmap
->border_width
;
413 WMMoveWidget(framel
, 0, 0);
416 create_mini_workspace(scr
, wsmap
, wsmap_array
);
417 workspace_map_realize(wsmap
, framel
, wsmap_array
);
422 static void update_mini_workspace(WWorkspaceMap
*wsmap
, W_WorkspaceMap
*wsmap_array
, int bulk_of_ten
)
424 int local_index
, general_index
;
425 int mini_workspace_cnt
;
429 if (bulk_of_ten
== wsmap_bulk_index
)
435 if (wsmap
->scr
->workspace_count
<= bulk_of_ten
* 2 * mini_workspace_per_line
)
438 wsmap_bulk_index
= bulk_of_ten
;
440 mini_workspace_cnt
= wsmap
->scr
->workspace_count
- wsmap_bulk_index
* 2 * mini_workspace_per_line
;
441 if (mini_workspace_cnt
> 2 * mini_workspace_per_line
)
442 mini_workspace_cnt
= 2 * mini_workspace_per_line
;
444 for (local_index
= 0; local_index
< 2 * mini_workspace_per_line
; local_index
++) {
445 general_index
= local_index
+ wsmap_bulk_index
* 2 * mini_workspace_per_line
;
446 if (general_index
< wsmap
->scr
->workspace_count
) {
448 WMSetLabelText(wsmap_array
[local_index
].workspace_label
, wsmap
->scr
->workspaces
[general_index
]->name
);
449 snprintf(name
, sizeof(name
), "%d", general_index
);
450 WMSetButtonText(wsmap_array
[local_index
].workspace_img_button
, name
);
452 /* updating label background*/
453 if (general_index
== wsmap
->scr
->current_workspace
) {
454 WMSetWidgetBackgroundPixmap(wsmap_array
[local_index
].workspace_label
, frame_bg_focused
);
455 WMSetLabelTextColor(wsmap_array
[local_index
].workspace_label
, wsmap
->scr
->window_title_color
[WS_FOCUSED
]);
457 WMSetWidgetBackgroundPixmap(wsmap_array
[local_index
].workspace_label
, frame_bg_unfocused
);
458 WMSetLabelTextColor(wsmap_array
[local_index
].workspace_label
, wsmap
->scr
->window_title_color
[WS_UNFOCUSED
]);
461 icon
= get_mini_workspace(wsmap
, general_index
);
463 WMSetButtonImage(wsmap_array
[local_index
].workspace_img_button
, icon
);
464 WMReleasePixmap(icon
);
467 if (local_index
< wsmap
->scr
->workspace_count
)
468 hide_mini_workspace(wsmap_array
, local_index
);
471 show_mini_workspace(wsmap
, wsmap_array
, mini_workspace_cnt
);
474 static void handle_event(WWorkspaceMap
*wsmap
, W_WorkspaceMap
*wsmap_array
)
478 KeyCode escKey
= XKeysymToKeycode(dpy
, XK_Escape
);
480 XGrabKeyboard(dpy
, WMWidgetXID(wsmap
->win
), False
, GrabModeAsync
, GrabModeAsync
, CurrentTime
);
481 XGrabPointer(dpy
, WMWidgetXID(wsmap
->win
), True
,
482 ButtonMotionMask
| ButtonReleaseMask
| ButtonPressMask
,
483 GrabModeAsync
, GrabModeAsync
, WMWidgetXID(wsmap
->win
), None
, CurrentTime
);
485 w_global
.process_workspacemap_event
= True
;
486 while (w_global
.process_workspacemap_event
) {
487 WMMaskEvent(dpy
, KeyPressMask
| KeyReleaseMask
| ExposureMask
488 | PointerMotionMask
| ButtonPressMask
| ButtonReleaseMask
| EnterWindowMask
, &ev
);
490 modifiers
= ev
.xkey
.state
& w_global
.shortcut
.modifiers_mask
;
494 if (ev
.xkey
.keycode
== escKey
|| (wKeyBindings
[WKBD_WORKSPACEMAP
].keycode
!= 0 &&
495 wKeyBindings
[WKBD_WORKSPACEMAP
].keycode
== ev
.xkey
.keycode
&&
496 wKeyBindings
[WKBD_WORKSPACEMAP
].modifier
== modifiers
)) {
497 w_global
.process_workspacemap_event
= False
;
502 XLookupString(&ev
.xkey
, NULL
, 16, &ks
, NULL
);
505 if (ks
>= 0x30 && ks
<= 0x39)
509 bulk_id
= wsmap_bulk_index
- 1;
510 else if (ks
== XK_Right
)
511 bulk_id
= wsmap_bulk_index
+ 1;
514 update_mini_workspace(wsmap
, wsmap_array
, bulk_id
);
519 switch (ev
.xbutton
.button
) {
521 update_mini_workspace(wsmap
, wsmap_array
, wsmap_bulk_index
- 1);
524 update_mini_workspace(wsmap
, wsmap_array
, wsmap_bulk_index
+ 1);
537 XUngrabPointer(dpy
, CurrentTime
);
538 XUngrabKeyboard(dpy
, CurrentTime
);
540 workspace_map_destroy(wsmap
);
543 static WWorkspaceMap
*init_workspace_map(WScreen
*scr
, W_WorkspaceMap
*wsmap_array
)
545 WWorkspaceMap
*wsmap
;
547 wsmap
= create_workspace_map(scr
, wsmap_array
, WD_BOTTOM
);
551 void StartWorkspaceMap(WScreen
*scr
)
553 WWorkspaceMap
*wsmap
;
554 W_WorkspaceMap wsmap_array
[2 * mini_workspace_per_line
];
556 /* save the current screen before displaying the workspace map */
557 wWorkspaceMapUpdate(scr
);
559 wsmap
= init_workspace_map(scr
, wsmap_array
);
561 workspace_map_show(wsmap
);
562 handle_event(wsmap
, wsmap_array
);