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 mini_preview
= RCreateImageFromXImage(scr
->rcontext
, pimg
, NULL
);
82 RImage
*tmp
= scr
->workspaces
[scr
->current_workspace
]->map
;
87 scr
->workspaces
[scr
->current_workspace
]->map
=
88 RSmoothScaleImage(mini_preview
,
89 scr
->scr_width
/ WORKSPACE_MAP_RATIO
,
90 scr
->scr_height
/ WORKSPACE_MAP_RATIO
);
91 RReleaseImage(mini_preview
);
96 static void workspace_map_slide(WWorkspaceMap
*wsmap
)
98 if (wsmap
->edge
== WD_TOP
)
99 slide_window(WMWidgetXID(wsmap
->win
), 0, -1 * wsmap
->wsheight
, wsmap
->xcount
, wsmap
->ycount
);
101 slide_window(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 slide_window(WMWidgetXID(wsmap
->win
), wsmap
->xcount
, wsmap
->ycount
, 0, -1 * wsmap
->wsheight
);
109 slide_window(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
;
377 if (scr
->workspace_count
== 0)
380 wsmap
= wmalloc(sizeof(*wsmap
));
382 wsmap
->border_width
= 5;
384 wsmap
->mini_workspace_width
= scr
->scr_width
/ WORKSPACE_MAP_RATIO
;
385 wsmap
->mini_workspace_height
= scr
->scr_height
/ WORKSPACE_MAP_RATIO
;
388 wsmap
->win
= WMCreateWindow(scr
->wmscreen
, "wsmap");
389 wsmap
->wswidth
= WidthOfScreen(DefaultScreenOfDisplay(dpy
));
390 wsmap
->wsheight
= WMFontHeight(scr
->info_text_font
) + (wsmap
->mini_workspace_height
+ 2 * WORKSPACE_SEPARATOR_WIDTH
) *
391 (scr
->workspace_count
> mini_workspace_per_line
? 2 : 1);
393 if (wPreferences
.wsmbackTexture
->any
.type
== WTEX_SOLID
) {
394 WMColor
*tmp
= WMCreateRGBColor(scr
->wmscreen
,
395 wPreferences
.wsmbackTexture
->any
.color
.red
,
396 wPreferences
.wsmbackTexture
->any
.color
.green
,
397 wPreferences
.wsmbackTexture
->any
.color
.blue
,
399 WMSetWidgetBackgroundColor(wsmap
->win
, tmp
);
402 WMResizeWidget(wsmap
->win
, wsmap
->wswidth
, wsmap
->wsheight
+ wsmap
->border_width
);
404 WMFrame
*framel
= WMCreateFrame(wsmap
->win
);
405 WMResizeWidget(framel
, wsmap
->wswidth
, wsmap
->border_width
);
406 WMSetFrameRelief(framel
, WRSimple
);
407 wWorkspaceMapUpdate(scr
);
410 if (edge
== WD_TOP
) {
412 WMMoveWidget(framel
, 0, wsmap
->wsheight
);
414 wsmap
->ycount
= scr
->scr_height
- wsmap
->wsheight
- wsmap
->border_width
;
415 WMMoveWidget(framel
, 0, 0);
418 create_mini_workspace(scr
, wsmap
, wsmap_array
);
419 workspace_map_realize(wsmap
, framel
, wsmap_array
);
424 static void update_mini_workspace(WWorkspaceMap
*wsmap
, W_WorkspaceMap
*wsmap_array
, int bulk_of_ten
)
426 int local_index
, general_index
;
427 int mini_workspace_cnt
;
431 if (bulk_of_ten
== wsmap_bulk_index
)
437 if (wsmap
->scr
->workspace_count
<= bulk_of_ten
* 2 * mini_workspace_per_line
)
440 wsmap_bulk_index
= bulk_of_ten
;
442 mini_workspace_cnt
= wsmap
->scr
->workspace_count
- wsmap_bulk_index
* 2 * mini_workspace_per_line
;
443 if (mini_workspace_cnt
> 2 * mini_workspace_per_line
)
444 mini_workspace_cnt
= 2 * mini_workspace_per_line
;
446 for (local_index
= 0; local_index
< 2 * mini_workspace_per_line
; local_index
++) {
447 general_index
= local_index
+ wsmap_bulk_index
* 2 * mini_workspace_per_line
;
448 if (general_index
< wsmap
->scr
->workspace_count
) {
450 WMSetLabelText(wsmap_array
[local_index
].workspace_label
, wsmap
->scr
->workspaces
[general_index
]->name
);
451 snprintf(name
, sizeof(name
), "%d", general_index
);
452 WMSetButtonText(wsmap_array
[local_index
].workspace_img_button
, name
);
454 /* updating label background*/
455 if (general_index
== wsmap
->scr
->current_workspace
) {
456 WMSetWidgetBackgroundPixmap(wsmap_array
[local_index
].workspace_label
, frame_bg_focused
);
457 WMSetLabelTextColor(wsmap_array
[local_index
].workspace_label
, wsmap
->scr
->window_title_color
[WS_FOCUSED
]);
459 WMSetWidgetBackgroundPixmap(wsmap_array
[local_index
].workspace_label
, frame_bg_unfocused
);
460 WMSetLabelTextColor(wsmap_array
[local_index
].workspace_label
, wsmap
->scr
->window_title_color
[WS_UNFOCUSED
]);
463 icon
= get_mini_workspace(wsmap
, general_index
);
465 WMSetButtonImage(wsmap_array
[local_index
].workspace_img_button
, icon
);
466 WMReleasePixmap(icon
);
469 if (local_index
< wsmap
->scr
->workspace_count
)
470 hide_mini_workspace(wsmap_array
, local_index
);
473 show_mini_workspace(wsmap
, wsmap_array
, mini_workspace_cnt
);
476 static void handle_event(WWorkspaceMap
*wsmap
, W_WorkspaceMap
*wsmap_array
)
480 KeyCode escKey
= XKeysymToKeycode(dpy
, XK_Escape
);
482 XGrabKeyboard(dpy
, WMWidgetXID(wsmap
->win
), False
, GrabModeAsync
, GrabModeAsync
, CurrentTime
);
483 XGrabPointer(dpy
, WMWidgetXID(wsmap
->win
), True
,
484 ButtonMotionMask
| ButtonReleaseMask
| ButtonPressMask
,
485 GrabModeAsync
, GrabModeAsync
, WMWidgetXID(wsmap
->win
), None
, CurrentTime
);
487 w_global
.process_workspacemap_event
= True
;
488 while (w_global
.process_workspacemap_event
) {
489 WMMaskEvent(dpy
, KeyPressMask
| KeyReleaseMask
| ExposureMask
490 | PointerMotionMask
| ButtonPressMask
| ButtonReleaseMask
| EnterWindowMask
, &ev
);
492 modifiers
= ev
.xkey
.state
& w_global
.shortcut
.modifiers_mask
;
496 if (ev
.xkey
.keycode
== escKey
|| (wKeyBindings
[WKBD_WORKSPACEMAP
].keycode
!= 0 &&
497 wKeyBindings
[WKBD_WORKSPACEMAP
].keycode
== ev
.xkey
.keycode
&&
498 wKeyBindings
[WKBD_WORKSPACEMAP
].modifier
== modifiers
)) {
499 w_global
.process_workspacemap_event
= False
;
504 XLookupString(&ev
.xkey
, NULL
, 16, &ks
, NULL
);
507 if (ks
>= 0x30 && ks
<= 0x39)
511 bulk_id
= wsmap_bulk_index
- 1;
512 else if (ks
== XK_Right
)
513 bulk_id
= wsmap_bulk_index
+ 1;
516 update_mini_workspace(wsmap
, wsmap_array
, bulk_id
);
521 switch (ev
.xbutton
.button
) {
523 update_mini_workspace(wsmap
, wsmap_array
, wsmap_bulk_index
- 1);
526 update_mini_workspace(wsmap
, wsmap_array
, wsmap_bulk_index
+ 1);
539 XUngrabPointer(dpy
, CurrentTime
);
540 XUngrabKeyboard(dpy
, CurrentTime
);
542 workspace_map_destroy(wsmap
);
545 static WWorkspaceMap
*init_workspace_map(WScreen
*scr
, W_WorkspaceMap
*wsmap_array
)
547 WWorkspaceMap
*wsmap
;
549 wsmap
= create_workspace_map(scr
, wsmap_array
, WD_BOTTOM
);
553 void StartWorkspaceMap(WScreen
*scr
)
555 WWorkspaceMap
*wsmap
;
556 W_WorkspaceMap wsmap_array
[2 * mini_workspace_per_line
];
558 /* save the current screen before displaying the workspace map */
559 wWorkspaceMapUpdate(scr
);
561 wsmap
= init_workspace_map(scr
, wsmap_array
);
563 workspace_map_show(wsmap
);
564 handle_event(wsmap
, wsmap_array
);