From 7f9432d18d999e09972df047ee7d48bb7744a88d Mon Sep 17 00:00:00 2001
From: David Maciejak <david.maciejak@gmail.com>
Date: Thu, 4 Dec 2014 13:37:36 +0700
Subject: [PATCH 4/4] wmaker: add core workspace pager functions

This patch is adding the core code needed to run the workspace pager.
---
 src/Makefile.am |   4 +-
 src/event.c     |   5 +
 src/misc.c      |   2 +-
 src/workspace.c |   6 +
 src/workspace.h |   1 +
 src/wsmap.c     | 546 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/wsmap.h     |   3 +
 7 files changed, 565 insertions(+), 2 deletions(-)
 create mode 100755 src/wsmap.c
 create mode 100644 src/wsmap.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 41a7e83..3e3e47d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -108,7 +108,9 @@ wmaker_SOURCES = 	\
 	wmspec.h \
 	wmspec.c \
 	workspace.c \
-	workspace.h
+	workspace.h \
+	wsmap.c \
+	wsmap.h
 
 if WM_OSDEP_BSD
 wmaker_SOURCES += osdep_bsd.c
diff --git a/src/event.c b/src/event.c
index 9a8a3e3..0f6eddc 100644
--- a/src/event.c
+++ b/src/event.c
@@ -77,6 +77,7 @@
 #include "event.h"
 #include "winmenu.h"
 #include "switchmenu.h"
+#include "wsmap.h"
 
 
 #define MOD_MASK wPreferences.modifier_mask
@@ -1606,6 +1607,10 @@ static void handleKeyPress(XEvent * event)
 			wSelectWindow(wwin, !wwin->flags.selected);
 		}
 		break;
+       case WKBD_WORKSPACEMAP:
+               if (!wPreferences.disable_workspace_pager)
+                       StartWorkspaceMap(scr);
+               break;
 	case WKBD_FOCUSNEXT:
 		StartWindozeCycle(wwin, event, True, False);
 		break;
diff --git a/src/misc.c b/src/misc.c
index 51eb1ee..e95478d 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -245,7 +245,7 @@ void SlideWindows(Window *wins[], int n, int from_x, int from_y, int to_x, int t
 		if (slide_delay > 0) {
 			wusleep(slide_delay * 1000L);
 		} else {
-			wusleep(10);
+			wusleep(1000L);
 		}
 		if (time(NULL) - time0 > MAX_ANIMATION_TIME)
 			break;
diff --git a/src/workspace.c b/src/workspace.c
index 107db2a..6e83986 100644
--- a/src/workspace.c
+++ b/src/workspace.c
@@ -49,6 +49,7 @@
 #include "wmspec.h"
 #include "xinerama.h"
 #include "event.h"
+#include "wsmap.h"
 
 #define MC_NEW          0
 #define MC_DESTROY_LAST 1
@@ -160,6 +161,8 @@ Bool wWorkspaceDelete(WScreen * scr, int workspace)
 		} else {
 			if (scr->workspaces[i]->name)
 				wfree(scr->workspaces[i]->name);
+                        if (scr->workspaces[i]->map)
+                               RReleaseImage(scr->workspaces[i]->map);
 			wfree(scr->workspaces[i]);
 		}
 	}
@@ -473,6 +476,9 @@ void wWorkspaceForceChange(WScreen * scr, int workspace)
 	if (workspace >= MAX_WORKSPACES || workspace < 0)
 		return;
 
+        if (!wPreferences.disable_workspace_pager && !process_workspacemap_event)
+                wWorkspaceMapUpdate(scr);
+
 	SendHelperMessage(scr, 'C', workspace + 1, NULL);
 
 	if (workspace > scr->workspace_count - 1)
diff --git a/src/workspace.h b/src/workspace.h
index 69d1b18..0c93a2f 100644
--- a/src/workspace.h
+++ b/src/workspace.h
@@ -27,6 +27,7 @@
 typedef struct WWorkspace {
     char *name;
     struct WDock *clip;
+    RImage *map;
 } WWorkspace;
 
 void wWorkspaceMake(WScreen *scr, int count);
diff --git a/src/wsmap.c b/src/wsmap.c
new file mode 100755
index 0000000..25c565f
--- /dev/null
+++ b/src/wsmap.c
@@ -0,0 +1,546 @@
+/*  wsmap.c - worskpace map
+ *
+ *  Window Maker window manager
+ *
+ *  Copyright (c) 2014 Window Maker Team - David Maciejak
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "wconfig.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "screen.h"
+#include "window.h"
+#include "misc.h"
+#include "workspace.h"
+#include "wsmap.h"
+#include "WINGs/WINGs/WINGsP.h"
+
+#ifdef USE_XSHAPE
+#include <X11/extensions/shape.h>
+#endif
+
+static const int WORKSPACE_MAP_RATIO  = 10;
+static const int WORKSPACE_SEPARATOR_WIDTH = 12;
+static const int mini_workspace_per_line = 5;
+
+/* used to store the index of the tenth displayed mini workspace
+  will be 0 for workspaces number 0 to 9
+  1 for workspaces number 10 -> 19
+*/
+int wsmap_bulk_index;
+WMPixmap *frame_bg_focused;
+WMPixmap *frame_bg_unfocused;
+
+typedef struct {
+	WScreen *scr;
+	WMWindow *win;
+	int xcount, ycount;
+	int wswidth, wsheight;
+	int mini_workspace_width, mini_workspace_height;
+	int edge;
+	int border_width;
+} WWorkspaceMap;
+
+typedef struct {
+	WMButton *workspace_img_button;
+	WMLabel *workspace_label;
+} W_WorkspaceMap;
+
+void wWorkspaceMapUpdate(WScreen *scr)
+{
+	XImage *pimg;
+
+	pimg = XGetImage(dpy, scr->root_win, 0, 0,
+					scr->scr_width, scr->scr_height,
+					AllPlanes, ZPixmap);
+        if (pimg) {
+		RImage *apercu = RCreateImageFromXImage(scr->rcontext, pimg, NULL);
+		XDestroyImage(pimg);
+
+		if (apercu) {
+			RImage *tmp = scr->workspaces[scr->current_workspace]->map;
+			if (tmp)
+				RReleaseImage(tmp);
+			 scr->workspaces[scr->current_workspace]->map = RSmoothScaleImage(apercu,
+									scr->scr_width / WORKSPACE_MAP_RATIO, 
+									scr->scr_height / WORKSPACE_MAP_RATIO);
+			RReleaseImage(apercu);
+		}
+	}
+}
+
+static void workspace_map_slide(WWorkspaceMap *wsmap)
+{
+	if (wsmap->edge == WD_TOP)
+		SlideWindow(WMWidgetXID(wsmap->win), 0, -1 *wsmap->wsheight, wsmap->xcount, wsmap->ycount);
+	else
+		SlideWindow(WMWidgetXID(wsmap->win), 0, wsmap->scr->scr_height, wsmap->xcount, wsmap->ycount);
+}
+
+static void workspace_map_unslide(WWorkspaceMap *wsmap)
+{
+	if (wsmap->edge == WD_TOP)
+		SlideWindow(WMWidgetXID(wsmap->win), wsmap->xcount, wsmap->ycount, 0, -1 * wsmap->wsheight);
+	else
+		SlideWindow(WMWidgetXID(wsmap->win), wsmap->xcount, wsmap->ycount, 0, wsmap->scr->scr_height);
+}
+
+static void workspace_map_destroy(WWorkspaceMap *wsmap)
+{
+	workspace_map_unslide(wsmap);
+	WMUnmapWidget(wsmap->win);
+	if (wsmap->win) {
+		Window info_win = wsmap->scr->info_window;
+		XEvent ev;
+		ev.xclient.type = ClientMessage;
+		ev.xclient.message_type = w_global.atom.wm.ignore_focus_events;
+		ev.xclient.format = 32;
+		ev.xclient.data.l[0] = True;
+
+		XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
+		WMUnmapWidget(wsmap->win);
+
+		ev.xclient.data.l[0] = False;
+		XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
+		WMDestroyWidget(wsmap->win);
+		if (frame_bg_focused)
+			WMReleasePixmap(frame_bg_focused);
+		if (frame_bg_unfocused)
+			WMReleasePixmap(frame_bg_unfocused);
+	}
+	wfree(wsmap);
+}
+
+static void selected_workspace_callback(WMWidget *w, void *data)
+{
+	WWorkspaceMap *wsmap = (WWorkspaceMap *) data;
+	WMButton *click_button = w;
+
+	if (w && wsmap) {
+		int workspace_id = atoi(WMGetButtonText(click_button));
+		wWorkspaceChange(wsmap->scr, workspace_id);
+		process_workspacemap_event = False;
+	}
+}
+
+static void set_workspace_map_background_image(WWorkspaceMap *wsmap)
+{
+	Pixmap pixmap, mask;
+	if (wPreferences.wsmbackTexture->any.type == WTEX_PIXMAP) {
+		RImage *tmp = wTextureRenderImage(wPreferences.wsmbackTexture, wsmap->wswidth, wsmap->wsheight, WREL_FLAT);
+		if (!tmp)
+			return;
+		RConvertImageMask(wsmap->scr->rcontext, tmp, &pixmap, &mask, 250);
+		RReleaseImage(tmp);
+
+		if (!pixmap)
+			return;
+		XSetWindowBackgroundPixmap(dpy, WMWidgetXID(wsmap->win), pixmap);
+
+#ifdef USE_XSHAPE
+		if (mask && w_global.xext.shape.supported)
+			XShapeCombineMask(dpy, WMWidgetXID(wsmap->win), ShapeBounding, 0, 0, mask, ShapeSet);
+#endif
+
+		if (pixmap)
+			XFreePixmap(dpy, pixmap);
+		if (mask)
+			XFreePixmap(dpy, mask);
+	}
+}
+
+static void workspace_map_show(WWorkspaceMap *wsmap)
+{
+	WMMapSubwidgets(wsmap->win);
+	WMMapWidget(wsmap->win);
+	workspace_map_slide(wsmap);
+}
+
+static WMPixmap *get_frame_background_color(WWorkspaceMap *wsmap, unsigned int width, unsigned int height, int type)
+{
+	RImage *img;
+	WMPixmap *pix;
+
+	if (!wsmap->scr->window_title_texture[type])
+		return NULL;
+
+	img = wTextureRenderImage(wsmap->scr->window_title_texture[type], width, height, WREL_FLAT);
+	if (!img)
+		return NULL;
+
+	pix = WMCreatePixmapFromRImage(wsmap->scr->wmscreen, img, 128);
+	RReleaseImage(img);
+
+	return pix;
+}
+
+static void workspace_map_realize(WWorkspaceMap *wsmap, WMFrame *frame_border, W_WorkspaceMap *wsmap_array)
+{
+	int i, mini_workspace_cnt, general_index;
+	WMPixmap *frame_border_pixmap;
+	WMSize label_size;
+
+	WMRealizeWidget(wsmap->win);
+	set_workspace_map_background_image(wsmap);
+
+	frame_border_pixmap = get_frame_background_color(wsmap, wsmap->wswidth, wsmap->border_width, WS_FOCUSED);
+	WMSetWidgetBackgroundPixmap(frame_border, frame_border_pixmap);
+	WMReleasePixmap(frame_border_pixmap);
+
+	label_size = WMGetViewSize(W_VIEW(wsmap_array[0].workspace_label));
+	frame_bg_focused = get_frame_background_color(wsmap, label_size.width, label_size.height, WS_FOCUSED);
+	frame_bg_unfocused = get_frame_background_color(wsmap, label_size.width, label_size.height, WS_UNFOCUSED);
+
+	mini_workspace_cnt = (wsmap->scr->workspace_count <= 2 * mini_workspace_per_line) ? wsmap->scr->workspace_count : 2 * mini_workspace_per_line;
+	for (i = 0; i < mini_workspace_cnt; i++) {
+		general_index = i + wsmap_bulk_index * 2 * mini_workspace_per_line;
+		if (general_index == wsmap->scr->current_workspace) {
+			WMSetWidgetBackgroundPixmap(wsmap_array[i].workspace_label, frame_bg_focused);
+			WMSetLabelTextColor(wsmap_array[i].workspace_label, wsmap->scr->window_title_color[WS_FOCUSED]);
+		} else {
+			WMSetWidgetBackgroundPixmap(wsmap_array[i].workspace_label, frame_bg_unfocused);
+			WMSetLabelTextColor(wsmap_array[i].workspace_label, wsmap->scr->window_title_color[WS_UNFOCUSED]);
+		}
+	}
+}
+
+static WMPixmap *enlight_workspace(WScreen *scr, RImage *mini_wkspace_map)
+{
+	RImage *tmp = RCloneImage(mini_wkspace_map);
+	RColor color;
+	WMPixmap *icon;
+
+	color.red = color.green = color.blue = 0;
+	color.alpha = 160;
+	RLightImage(tmp, &color);
+	icon = WMCreatePixmapFromRImage(scr->wmscreen, tmp, 128);
+	RReleaseImage(tmp);
+
+	return icon;
+}
+
+static WMPixmap *dummy_background_pixmap(WWorkspaceMap *wsmap)
+{
+	RImage *img;
+	WMPixmap *icon;
+
+	img = RCreateImage(wsmap->wswidth, wsmap->wsheight, 0);
+
+	if (!img)
+		return NULL;
+
+	/* the workspace texture is not saved anywhere, so just using the default unfocus color */
+	if (wsmap->scr->window_title_texture[WS_UNFOCUSED]) {
+		RColor frame_bg_color;
+
+		frame_bg_color.red = wsmap->scr->window_title_texture[WS_UNFOCUSED]->solid.normal.red;
+		frame_bg_color.green = wsmap->scr->window_title_texture[WS_UNFOCUSED]->solid.normal.green;
+		frame_bg_color.blue = wsmap->scr->window_title_texture[WS_UNFOCUSED]->solid.normal.blue;
+		RFillImage(img, &frame_bg_color);
+	}
+
+	icon = WMCreatePixmapFromRImage(wsmap->scr->wmscreen, img, 128);
+	RReleaseImage(img);
+
+	return icon;
+}
+
+static void show_mini_workspace(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array, int max_mini_workspace)
+{
+	int index, space_width;
+	int border_width_adjustement = (wsmap->edge == WD_TOP) ? 0 : wsmap->border_width;
+	int font_height = WMFontHeight(wsmap->scr->info_text_font);
+
+	if (max_mini_workspace > mini_workspace_per_line)
+		space_width = (wsmap->wswidth - mini_workspace_per_line * wsmap->mini_workspace_width) / (mini_workspace_per_line + 1);
+	else
+		space_width = (wsmap->wswidth - max_mini_workspace * wsmap->mini_workspace_width) / (max_mini_workspace + 1);
+
+	for (index = 0; index <  max_mini_workspace; index++) {
+		int i , j;
+
+		i = 0;
+		j = index;
+
+		if (index >= mini_workspace_per_line) {
+			i = 1;
+			j -= mini_workspace_per_line;
+		}
+		if (wsmap_array[index].workspace_img_button) {
+			WMResizeWidget(wsmap_array[index].workspace_img_button, wsmap->mini_workspace_width, wsmap->mini_workspace_height);
+			WMMoveWidget(wsmap_array[index].workspace_img_button, j * wsmap->mini_workspace_width + (j + 1) * space_width,
+							border_width_adjustement + WORKSPACE_SEPARATOR_WIDTH +
+							i * (wsmap->mini_workspace_height + 2 * WORKSPACE_SEPARATOR_WIDTH) + font_height);
+			WMMapWidget(wsmap_array[index].workspace_img_button);
+		}
+		if (wsmap_array[index].workspace_label) {
+			WMResizeWidget(wsmap_array[index].workspace_label, wsmap->mini_workspace_width, font_height);
+			WMMoveWidget(wsmap_array[index].workspace_label, j * wsmap->mini_workspace_width + (j + 1) * space_width,
+							border_width_adjustement + WORKSPACE_SEPARATOR_WIDTH +
+							i * (wsmap->mini_workspace_height + 2 * WORKSPACE_SEPARATOR_WIDTH));
+			WMMapWidget(wsmap_array[index].workspace_label);
+		}
+	}
+}
+
+static void hide_mini_workspace(W_WorkspaceMap *wsmap_array, int i)
+{
+	if (wsmap_array[i].workspace_img_button)
+		WMUnmapWidget(wsmap_array[i].workspace_img_button);
+	if (wsmap_array[i].workspace_label)
+		WMUnmapWidget(wsmap_array[i].workspace_label);
+}
+
+static  WMPixmap *get_mini_workspace(WWorkspaceMap *wsmap, int index) {
+	if (!wsmap->scr->workspaces[index]->map)
+		return dummy_background_pixmap(wsmap);
+
+	if (index == wsmap->scr->current_workspace)
+		return enlight_workspace(wsmap->scr,  wsmap->scr->workspaces[index]->map);
+
+	return WMCreatePixmapFromRImage(wsmap->scr->wmscreen, wsmap-> scr->workspaces[index]->map, 128);
+}
+
+static void create_mini_workspace(WScreen *scr, WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array)
+{
+	int workspace_index;
+	int mini_workspace_cnt;
+	char name[10];
+	WMButton *mini_workspace_btn;
+	WMPixmap *icon;
+
+	/* by default display the 10 first mini workspaces */
+	wsmap_bulk_index = 0;
+	mini_workspace_cnt = (scr->workspace_count <= 2 * mini_workspace_per_line) ? scr->workspace_count : 2 * mini_workspace_per_line;
+	for (workspace_index = 0; workspace_index < mini_workspace_cnt; workspace_index++) {
+		mini_workspace_btn = WMCreateButton(wsmap->win, WBTOnOff);
+		WMSetButtonBordered(mini_workspace_btn, 0);
+		WMLabel *workspace_name_label =	WMCreateLabel(wsmap->win);
+		WMSetLabelFont(workspace_name_label, scr->info_text_font);
+		WMSetLabelText(workspace_name_label,  scr->workspaces[workspace_index]->name);
+
+		wsmap_array[workspace_index].workspace_img_button = mini_workspace_btn;
+		wsmap_array[workspace_index].workspace_label = workspace_name_label;
+
+		WMSetButtonImagePosition(mini_workspace_btn, WIPImageOnly);
+		icon = get_mini_workspace(wsmap, workspace_index);
+		if (icon) {
+			WMSetButtonImage(mini_workspace_btn, icon);
+			WMReleasePixmap(icon);
+		}
+
+		snprintf(name, sizeof(name), "%d", workspace_index);
+		WMSetButtonText(mini_workspace_btn, name);
+		WMSetButtonAction(mini_workspace_btn, selected_workspace_callback, wsmap);
+	}
+	show_mini_workspace(wsmap, wsmap_array, mini_workspace_cnt);
+}
+
+static WWorkspaceMap *create_workspace_map(WScreen *scr, W_WorkspaceMap *wsmap_array, int edge)
+{
+	WWorkspaceMap *wsmap = wmalloc(sizeof(WWorkspaceMap));
+
+	wsmap->border_width = 5;
+	wsmap->edge = edge;
+	wsmap->mini_workspace_width = scr->scr_width / WORKSPACE_MAP_RATIO;
+	wsmap->mini_workspace_height = scr->scr_height / WORKSPACE_MAP_RATIO;
+
+        if (scr->workspace_count == 0)
+                return NULL;
+
+	wsmap->scr = scr;
+	wsmap->win = WMCreateWindow(scr->wmscreen, "wsmap");
+	wsmap->wswidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
+	wsmap->wsheight = WMFontHeight(scr->info_text_font) + (wsmap->mini_workspace_height + 2 * WORKSPACE_SEPARATOR_WIDTH) *
+								(scr->workspace_count > mini_workspace_per_line ? 2 : 1);
+
+	if (wPreferences.wsmbackTexture->any.type == WTEX_SOLID) {
+		WMColor *tmp = WMCreateRGBColor(scr->wmscreen,
+							wPreferences.wsmbackTexture->any.color.red,
+							wPreferences.wsmbackTexture->any.color.green,
+							wPreferences.wsmbackTexture->any.color.blue,
+							False);
+		WMSetWidgetBackgroundColor(wsmap->win, tmp);
+		WMReleaseColor(tmp);
+	}
+	WMResizeWidget(wsmap->win, wsmap->wswidth, wsmap->wsheight + wsmap->border_width);
+
+	WMFrame *framel = WMCreateFrame(wsmap->win);
+	WMResizeWidget(framel, wsmap->wswidth, wsmap->border_width);
+	WMSetFrameRelief(framel, WRSimple);
+	wWorkspaceMapUpdate(scr);
+
+	wsmap->xcount = 0;
+	if (edge == WD_TOP) {
+		wsmap->ycount = 0;
+		WMMoveWidget(framel, 0, wsmap->wsheight);
+	} else {
+		wsmap->ycount = scr->scr_height - wsmap->wsheight - wsmap->border_width;
+		WMMoveWidget(framel, 0, 0);
+	}
+
+	create_mini_workspace(scr, wsmap, wsmap_array);
+	workspace_map_realize(wsmap, framel, wsmap_array);
+
+	return wsmap;
+}
+
+static void update_mini_workspace(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array, int bulk_of_ten)
+{
+	int local_index, general_index;
+	int mini_workspace_cnt;
+	char name[10];
+	WMPixmap *icon;
+
+	if (bulk_of_ten == wsmap_bulk_index)
+		return;
+
+	if (bulk_of_ten < 0)
+		return;
+	
+	if (wsmap->scr->workspace_count <= bulk_of_ten * 2 * mini_workspace_per_line)
+		return;
+
+	wsmap_bulk_index = bulk_of_ten;
+
+	mini_workspace_cnt = wsmap->scr->workspace_count - wsmap_bulk_index * 2 * mini_workspace_per_line;
+	if (mini_workspace_cnt > 2 * mini_workspace_per_line)
+		mini_workspace_cnt = 2 * mini_workspace_per_line;
+
+	for (local_index = 0; local_index <  2 * mini_workspace_per_line; local_index++) {
+		general_index = local_index + wsmap_bulk_index * 2 * mini_workspace_per_line;
+		if (general_index < wsmap->scr->workspace_count) {
+			/* updating label */
+			WMSetLabelText(wsmap_array[local_index].workspace_label, wsmap-> scr->workspaces[general_index]->name);
+			snprintf(name, sizeof(name), "%d", general_index);
+			WMSetButtonText(wsmap_array[local_index].workspace_img_button, name);
+
+			/* updating label background*/
+			if (general_index == wsmap->scr->current_workspace) {
+				WMSetWidgetBackgroundPixmap(wsmap_array[local_index].workspace_label, frame_bg_focused);
+				WMSetLabelTextColor(wsmap_array[local_index].workspace_label, wsmap->scr->window_title_color[WS_FOCUSED]);
+			} else {
+				WMSetWidgetBackgroundPixmap(wsmap_array[local_index].workspace_label, frame_bg_unfocused);
+				WMSetLabelTextColor(wsmap_array[local_index].workspace_label, wsmap->scr->window_title_color[WS_UNFOCUSED]);
+			}
+
+			icon = get_mini_workspace(wsmap, general_index);
+			if (icon) {
+				WMSetButtonImage(wsmap_array[local_index].workspace_img_button, icon);
+				WMReleasePixmap(icon);
+			}
+		}
+		else
+			if (local_index < wsmap->scr->workspace_count)
+				hide_mini_workspace(wsmap_array, local_index);
+	}
+	show_mini_workspace(wsmap, wsmap_array, mini_workspace_cnt);
+}
+
+static void handle_event(WWorkspaceMap *wsmap, W_WorkspaceMap *wsmap_array)
+{
+	XEvent ev;
+	int modifiers;
+	KeyCode	escKey = XKeysymToKeycode(dpy, XK_Escape);
+
+	XGrabKeyboard(dpy, WMWidgetXID(wsmap->win), False, GrabModeAsync, GrabModeAsync, CurrentTime);
+	XGrabPointer(dpy, WMWidgetXID(wsmap->win), True,
+                                 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
+                                 GrabModeAsync, GrabModeAsync, WMWidgetXID(wsmap->win), None, CurrentTime);
+
+	process_workspacemap_event = True;
+	while (process_workspacemap_event) {
+		WMMaskEvent(dpy, KeyPressMask | KeyReleaseMask | ExposureMask
+			    | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask, &ev);
+
+		if (!wsmap)
+			break;
+		modifiers = ev.xkey.state & w_global.shortcut.modifiers_mask;
+
+		switch (ev.type) {
+		case KeyPress:
+			if (ev.xkey.keycode == escKey || (wKeyBindings[WKBD_WORKSPACEMAP].keycode != 0 &&
+										wKeyBindings[WKBD_WORKSPACEMAP].keycode == ev.xkey.keycode &&
+										wKeyBindings[WKBD_WORKSPACEMAP].modifier == modifiers)) {
+				process_workspacemap_event = False;
+			} else {
+				KeySym ks;
+				int bulk_id;
+
+				XLookupString(&ev.xkey, NULL, 16, &ks, NULL);
+
+				bulk_id = -1;
+				if (ks >= 0x30 && ks <= 0x39)
+					bulk_id = ks - 0x30;
+				else
+					if (ks == XK_Left)
+						bulk_id = wsmap_bulk_index - 1;
+					else 
+						if (ks == XK_Right)
+							bulk_id = wsmap_bulk_index + 1;
+
+				if (bulk_id >= 0)
+					update_mini_workspace(wsmap, wsmap_array, bulk_id);
+			}
+			break;
+		case ButtonPress:
+			switch (ev.xbutton.button) {
+			case Button6:
+				update_mini_workspace(wsmap, wsmap_array, wsmap_bulk_index - 1);
+				break;
+			case Button7:
+				update_mini_workspace(wsmap, wsmap_array, wsmap_bulk_index + 1);
+				break;
+			default:
+				WMHandleEvent(&ev);
+			}
+			break;
+		default:
+			WMHandleEvent(&ev);
+			break;
+		}
+	}
+
+	XUngrabPointer(dpy, CurrentTime);
+	XUngrabKeyboard(dpy, CurrentTime);
+	if (wsmap)
+		workspace_map_destroy(wsmap);
+}
+
+static WWorkspaceMap *init_workspace_map(WScreen *scr, W_WorkspaceMap *wsmap_array)
+{
+	WWorkspaceMap *wsmap;
+
+	wsmap = create_workspace_map(scr, wsmap_array, WD_BOTTOM);
+	return wsmap;
+}
+
+void StartWorkspaceMap(WScreen *scr)
+{
+	WWorkspaceMap *wsmap;
+	W_WorkspaceMap wsmap_array[2 * mini_workspace_per_line];
+
+	/* save the current screen before displaying the workspace map */
+	wWorkspaceMapUpdate(scr);
+
+	wsmap = init_workspace_map(scr, wsmap_array);
+	if (wsmap) {
+		workspace_map_show(wsmap);
+		handle_event(wsmap, wsmap_array);
+	}
+}
diff --git a/src/wsmap.h b/src/wsmap.h
new file mode 100644
index 0000000..cdceca3
--- /dev/null
+++ b/src/wsmap.h
@@ -0,0 +1,3 @@
+Bool process_workspacemap_event;
+void wWorkspaceMapUpdate(WScreen *scr);
+void StartWorkspaceMap(WScreen *scr);
-- 
2.1.0

