This patch is replacing the modelock legacy hardcoded language dropdown
icons with a compact titlebar button based on the current locale.
(it adds also a detection to xkbfile library which is required to get
the short name of the locale).
Now supports up to 4 layouts, clicking on the language button will cycle
through them (XKB officially supports up to four groups).
---
configure.ac | 4 +-
m4/wm_xext_check.m4 | 32 +++++++
src/Makefile.am | 1 +
src/event.c | 17 ++--
src/framewin.c | 198 ++++++++++++++++++++++++++++++++++++++++----
src/framewin.h | 3 +-
src/screen.c | 7 +-
src/window.c | 88 +++++++++++++++++---
src/window.h | 5 ++
9 files changed, 314 insertions(+), 41 deletions(-)
diff --git a/
configure.ac b/
configure.acindex 7048524d..96277426 100644
--- a/
configure.ac+++ b/
configure.ac@@ -555,7 +555,9 @@ AC_ARG_ENABLE([modelock],
m4_divert_pop([INIT_PREPARE])dnl
AS_IF([test "x$enable_modelock" = "xyes"],
- [AC_DEFINE([XKB_MODELOCK], [1], [whether XKB language MODELOCK should be enabled]) ])
+ [WM_XEXT_CHECK_XKBFILE
+ AS_IF([test "x$enable_modelock" = "xyes"],
+ [AC_DEFINE([XKB_MODELOCK], [1], [whether XKB language MODELOCK should be enabled])])])
dnl XDND Drag-nd-Drop support
diff --git a/m4/wm_xext_check.m4 b/m4/wm_xext_check.m4
index 751d8314..d482bdd5 100644
--- a/m4/wm_xext_check.m4
+++ b/m4/wm_xext_check.m4
@@ -232,3 +232,35 @@ AC_DEFUN_ONCE([WM_XEXT_CHECK_XRANDR],
[supported_xext], [LIBXRANDR], [], [-])dnl
AC_SUBST([LIBXRANDR])dnl
]) dnl AC_DEFUN
+
+
+# WM_XEXT_CHECK_XKBFILE
+# ---------------------
+#
+# Check for the XKB File extension library (libxkbfile)
+# The check depends on variable 'enable_modelock' being either:
+# yes - detect, fail if not found
+# no - do not detect, disable support
+#
+# When found, append appropriate stuff in LIBXKBFILE, and append info to
+# the variable 'supported_xext'
+# When not found, generate an error because it's required for modelock
+AC_DEFUN_ONCE([WM_XEXT_CHECK_XKBFILE],
+[WM_LIB_CHECK([XKBFile], [-lxkbfile], [XkbRF_GetNamesProp], [$XLIBS],
+ [wm_save_CFLAGS="$CFLAGS"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([dnl
+@%:@include <stdio.h>
+@%:@include <X11/Xlib.h>
+@%:@include <X11/XKBlib.h>
+@%:@include <X11/extensions/XKBfile.h>
+@%:@include <X11/extensions/XKBrules.h>
+], [dnl
+ Display *dpy = NULL;
+ XkbRF_VarDefsRec vd;
+ XkbRF_GetNamesProp(dpy, NULL, &vd);])],
+ [],
+ [AC_MSG_ERROR([found $CACHEVAR but cannot compile using XKBfile header])])
+ CFLAGS="$wm_save_CFLAGS"],
+ [supported_xext], [LIBXKBFILE], [enable_modelock], [-])dnl
+AC_SUBST([LIBXKBFILE])dnl
+]) dnl AC_DEFUN
diff --git a/src/Makefile.am b/src/Makefile.am
index 8782191b..d05a9a01 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -165,6 +165,7 @@ wmaker_LDADD = \
@XLFLAGS@ \
@LIBXRANDR@ \
@LIBXINERAMA@ \
+
@LIBXKBFILE@ \
@XLIBS@ \
@LIBM@ \
@INTLIBS@
diff --git a/src/event.c b/src/event.c
index 0fcbf0e7..a62586b7 100644
--- a/src/event.c
+++ b/src/event.c
@@ -593,10 +593,12 @@ static void handleExtensions(XEvent * event)
handleShapeNotify(event);
}
#endif
-
if (w_global.xext.xkb.supported && event->type == w_global.xext.xkb.event_base) {
+
if (w_global.xext.xkb.supported && event->type >= w_global.xext.xkb.event_base
+
&& event->type <= w_global.xext.xkb.event_base + 255) {
XkbEvent *xkbevent = (XkbEvent *) event;
+
int xkb_type = xkbevent->any.xkb_type;
-
if (xkbevent->any.xkb_type == XkbNewKeyboardNotify) {
+
if (xkb_type == XkbNewKeyboardNotify) {
int j;
WScreen *scr;
@@ -607,8 +609,10 @@ static void handleExtensions(XEvent * event)
}
#ifdef KEEP_XKB_LOCK_STATUS
else {
-
if (wPreferences.modelock && (xkbevent->any.xkb_type == XkbIndicatorStateNotify)) {
-
handleXkbIndicatorStateNotify((XkbEvent *) event);
+
/* Listen not only for IndicatorStateNotify but also for StateNotify
+
* which is commonly emitted on group (layout) changes. */
+
if (wPreferences.modelock && (xkb_type == XkbIndicatorStateNotify || xkb_type == XkbStateNotify)) {
+
handleXkbIndicatorStateNotify(xkbevent);
}
}
#endif /*KEEP_XKB_LOCK_STATUS */
@@ -1324,6 +1328,8 @@ static void handleXkbIndicatorStateNotify(XkbEvent *event)
if (wwin->frame->languagemode != staterec.group) {
wwin->frame->last_languagemode = wwin->frame->languagemode;
wwin->frame->languagemode = staterec.group;
+
wWindowGetLanguageLabel(wwin->frame->languagemode, wwin->frame->language_label);
+
wFrameWindowUpdateLanguageButton(wwin->frame);
}
#ifdef XKB_BUTTON_HINT
if (wwin->frame->titlebar) {
@@ -1967,7 +1973,8 @@ static void dispatchWKBDCommand(int command, WScreen *scr, WWindow *wwin, XEvent
wwin->frame->languagemode = wwin->frame->last_languagemode;
wwin->frame->last_languagemode = staterec.group;
XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
-
+
/* Update the language label text */
+
wWindowGetLanguageLabel(wwin->frame->languagemode, wwin->frame->language_label);
}
}
break;
diff --git a/src/framewin.c b/src/framewin.c
index e72d881d..ac3fc747 100644
--- a/src/framewin.c
+++ b/src/framewin.c
@@ -54,7 +54,7 @@ static void resizebarMouseDown(WObjDescriptor * desc, XEvent * event);
static void checkTitleSize(WFrameWindow * fwin);
static void paintButton(WCoreWindow * button, WTexture * texture,
-
unsigned long color, WPixmap * image, int pushed);
+
unsigned long color, WPixmap * image, int pushed, int from_xpm);
static void updateTitlebar(WFrameWindow * fwin);
@@ -98,6 +98,7 @@ WFrameWindow *wFrameWindowCreate(WScreen * scr, int wlevel, int x, int y,
#ifdef KEEP_XKB_LOCK_STATUS
fwin->languagemode = XkbGroup1Index;
fwin->last_languagemode = XkbGroup2Index;
+
wWindowGetLanguageLabel(fwin->languagemode, fwin->language_label);
#endif
fwin->depth = depth;
@@ -145,6 +146,7 @@ void wFrameWindowUpdateBorders(WFrameWindow * fwin, int flags)
theight = *fwin->title_min_height;
} else {
theight = 0;
+
fwin->flags.titlebar = 0;
}
if (wPreferences.new_style == TS_NEW) {
@@ -536,6 +538,8 @@ static void updateTitlebar(WFrameWindow * fwin)
#ifdef XKB_BUTTON_HINT
else {
int bsize = theight - 7;
+
if (wPreferences.new_style == TS_NEXT)
+
bsize -= 1;
if (fwin->flags.hide_left_button || !fwin->left_button || fwin->flags.lbutton_dont_fit) {
if (fwin->language_button)
wCoreConfigure(fwin->language_button, 3, (theight - bsize) / 2,
@@ -950,6 +954,10 @@ void wFrameWindowPaint(WFrameWindow * fwin)
remakeTexture(fwin, i);
}
}
+#ifdef XKB_BUTTON_HINT
+
if (wPreferences.modelock)
+
wFrameWindowUpdateLanguageButton(fwin);
+#endif
}
if (fwin->flags.need_texture_change) {
@@ -1023,7 +1031,8 @@ void wFrameWindowPaint(WFrameWindow * fwin)
allButtons = 0;
}
#ifdef XKB_BUTTON_HINT
-
fwin->languagebutton_image = scr->b_pixmaps[WBUT_XKBGROUP1 + fwin->languagemode];
+
if (fwin->flags.language_button && !fwin->languagebutton_image[0])
+
wFrameWindowUpdateLanguageButton(fwin);
#endif
if (fwin->title) {
@@ -1228,10 +1237,145 @@ int wFrameWindowChangeTitle(WFrameWindow *fwin, const char *new_title)
}
#ifdef XKB_BUTTON_HINT
+
+static int wFrameWindowSetLanguageButtonImages(WFrameWindow *fwin, Pixmap *pixmaps, int count)
+{
+
int i;
+
for (i = 0; i < count; i++) {
+
WPixmap *wp = wPixmapCreate(pixmaps[i], None);
+
if (!wp) {
+
int j;
+
XFreePixmap(dpy, pixmaps[i]);
+
for (j = i + 1; j < count; j++)
+
XFreePixmap(dpy, pixmaps[j]);
+
return 0;
+
}
+
wp->client_owned = 0;
+
wp->client_owned_mask = 0;
+
if (fwin->languagebutton_image[i] && !fwin->languagebutton_image[i]->shared)
+
wPixmapDestroy(fwin->languagebutton_image[i]);
+
fwin->languagebutton_image[i] = wp;
+
}
+
return 1;
+}
+
void wFrameWindowUpdateLanguageButton(WFrameWindow * fwin)
{
-
paintButton(fwin->language_button, fwin->title_texture[fwin->flags.state],
-
WMColorPixel(fwin->title_color[fwin->flags.state]), fwin->languagebutton_image, True);
+
WScreen *scr = fwin->screen_ptr;
+
WCoreWindow *button = fwin->language_button;
+
GC gc = scr->copy_gc;
+
int i, text_width, text_height;
+
int border_thickness = 3;
+
int key_width, key_height;
+
int group_index;
+
Pixmap tmp[2];
/* focused, unfocused */
+
+
if (!fwin->flags.titlebar || fwin->core->descriptor.parent_type != WCLASS_WINDOW)
+
return;
+
+
if (!button || fwin->language_label[0] == '\0')
+
return;
+
+
group_index = fwin->languagemode;
+
if (group_index < 0 || group_index >= 4)
+
return;
+
+
/* Calculate text dimensions */
+
int small_px = WMFontHeight(*fwin->font) * 55 / 100;
+
WMFont *small = WMBoldSystemFontOfSize(scr->wmscreen, small_px);
+
text_width = WMWidthOfString(small, fwin->language_label, strlen(fwin->language_label));
+
text_height = WMFontHeight(small);
+
+
key_width = button->width - border_thickness;
+
key_height = button->height - border_thickness;
+
+
/* Ensure dimensions are valid */
+
if (key_width < 1 || key_height < 1) {
+
WMReleaseFont(small);
+
return;
+
}
+
+
/* Create temporary pixmaps for immediate drawing */
+
tmp[0] = XCreatePixmap(dpy, button->window, 2*key_width, key_height, scr->w_depth);
+
if (tmp[0] == None) {
+
WMReleaseFont(small);
+
return;
+
}
+
+
tmp[1] = None;
+
if (wPreferences.new_style == TS_NEW) {
+
tmp[1] = XCreatePixmap(dpy, button->window, 2*key_width, key_height, scr->w_depth);
+
if (tmp[1] == None) {
+
XFreePixmap(dpy, tmp[0]);
+
WMReleaseFont(small);
+
return;
+
}
+
}
+
+
/* Reset GC to ensure clean state for drawing */
+
XSetClipMask(dpy, gc, None);
+
+
/* Draw the language label centered in the button */
+
int text_x = (key_width - text_width) / 2;
+
int text_y = (key_height - text_height) / 2;
+
+
/* Fill the color pixmap depending on the style */
+
if (wPreferences.new_style == TS_NEW) {
+
for (i = 0; i < 2; i++) {
+
if (fwin->title_texture[i]->any.type != WTEX_SOLID && fwin->title_back[i] != None) {
+
XCopyArea(dpy, fwin->languagebutton_back[i], tmp[i], scr->copy_gc,
+
0, 0, button->width, button->height, -1, -1);
+
} else {
+
unsigned long bg_pixel = fwin->title_texture[i]->solid.normal.pixel;
+
XSetForeground(dpy, gc, bg_pixel);
+
XFillRectangle(dpy, tmp[i], gc, 0, 0, key_width, key_height);
+
}
+
WMDrawString(scr->wmscreen, tmp[i], fwin->title_color[i],
+
small, text_x, text_y, fwin->language_label, strlen(fwin->language_label));
+
}
+
} else if (wPreferences.new_style == TS_OLD) {
+
unsigned long bg_pixel = scr->widget_texture->normal.pixel;
+
XSetForeground(dpy, gc, bg_pixel);
+
XFillRectangle(dpy, tmp[0], gc, 0, 0, key_width, key_height);
+
WMDrawString(scr->wmscreen, tmp[0], WMBlackColor(scr->wmscreen),
+
small, text_x, text_y, fwin->language_label, strlen(fwin->language_label));
+
} else {
+
unsigned long bg_pixel = scr->widget_texture->dark.pixel;
+
XSetForeground(dpy, gc, bg_pixel);
+
XFillRectangle(dpy, tmp[0], gc, 0, 0, key_width, key_height);
+
WMColor *silver = WMCreateRGBColor(scr->wmscreen, 0xc0c0, 0xc0c0, 0xc0c0, True);
+
WMDrawString(scr->wmscreen, tmp[0], silver,
+
small, text_x, text_y, fwin->language_label, strlen(fwin->language_label));
+
WMReleaseColor(silver);
+
}
+
+
/* pushed button next to normal for easy access when painting */
+
text_x = key_width + (key_width - text_width) / 2;
+
if (wPreferences.new_style == TS_NEW) {
+
XSetForeground(dpy, gc, scr->white_pixel);
+
for (i = 0; i < 2; i++) {
+
XFillRectangle(dpy, tmp[i], gc, key_width, 0, key_width, key_height);
+
WMDrawString(scr->wmscreen, tmp[i], WMBlackColor(scr->wmscreen),
+
small, text_x, text_y, fwin->language_label, strlen(fwin->language_label));
+
}
+
} else if (wPreferences.new_style == TS_OLD) {
+
XSetForeground(dpy, gc, scr->white_pixel);
+
XFillRectangle(dpy, tmp[0], gc, key_width, 0, key_width, key_height);
+
WMDrawString(scr->wmscreen, tmp[0], WMDarkGrayColor(scr->wmscreen),
+
small, text_x, text_y, fwin->language_label, strlen(fwin->language_label));
+
} else {
+
unsigned long bg_pixel = scr->widget_texture->dark.pixel;
+
WMColor *silver = WMCreateRGBColor(scr->wmscreen, 0xc0c0, 0xc0c0, 0xc0c0, True);
+
XSetForeground(dpy, gc, bg_pixel);
+
XFillRectangle(dpy, tmp[0], gc, key_width, 0, key_width, key_height);
+
WMDrawString(scr->wmscreen, tmp[0], silver,
+
small, text_x, text_y, fwin->language_label, strlen(fwin->language_label));
+
WMReleaseColor(silver);
+
}
+
+
WMReleaseFont(small);
+
+
wFrameWindowSetLanguageButtonImages(fwin, tmp, (wPreferences.new_style == TS_NEW) ? 2 : 1);
}
#endif
/* XKB_BUTTON_HINT */
@@ -1286,7 +1430,7 @@ static void checkTitleSize(WFrameWindow * fwin)
fwin->flags.incomplete_title = 0;
}
-static void paintButton(WCoreWindow * button, WTexture * texture, unsigned long color, WPixmap * image, int pushed)
+static void paintButton(WCoreWindow * button, WTexture * texture, unsigned long color, WPixmap * image, int pushed, int from_xpm)
{
WScreen *scr = button->screen_ptr;
GC copy_gc = scr->copy_gc;
@@ -1364,8 +1508,12 @@ static void paintButton(WCoreWindow * button, WTexture * texture, unsigned long
} else {
if (wPreferences.new_style == TS_OLD) {
XSetForeground(dpy, copy_gc, scr->dark_pixel);
-
XFillRectangle(dpy, button->window, copy_gc, 0, 0,
-
button->width, button->height);
+
if (from_xpm)
+
XFillRectangle(dpy, button->window, copy_gc, 0, 0,
+
button->width, button->height);
+
else
+
XCopyArea(dpy, image->image, button->window, copy_gc,
+
left, 0, width, image->height, x, y);
} else {
XSetForeground(dpy, copy_gc, scr->black_pixel);
XCopyArea(dpy, image->image, button->window, copy_gc,
@@ -1379,7 +1527,11 @@ static void paintButton(WCoreWindow * button, WTexture * texture, unsigned long
XSetForeground(dpy, copy_gc, color);
XSetBackground(dpy, copy_gc, texture->any.color.pixel);
}
-
XFillRectangle(dpy, button->window, copy_gc, 0, 0, button->width, button->height);
+
if (from_xpm)
+
XFillRectangle(dpy, button->window, copy_gc, 0, 0, button->width, button->height);
+
else
+
XCopyArea(dpy, image->image, button->window, copy_gc,
+
left, 0, width, image->height, x, y);
}
}
}
@@ -1394,18 +1546,23 @@ static void handleButtonExpose(WObjDescriptor * desc, XEvent * event)
#ifdef XKB_BUTTON_HINT
if (button == fwin->language_button) {
-
if (!fwin->flags.hide_language_button)
+
if (!fwin->flags.hide_language_button) {
+
/* map focused and pfocused states to focus language button image */
+
int lb_index = wPreferences.new_style == TS_NEW && (fwin->flags.state == 1) ? 1 : 0;
+
paintButton(button, fwin->title_texture[fwin->flags.state],
WMColorPixel(fwin->title_color[fwin->flags.state]),
-
fwin->languagebutton_image, False);
+
fwin->languagebutton_image[lb_index],
+
False, False);
+
}
} else
#endif
if (button == fwin->left_button)
paintButton(button, fwin->title_texture[fwin->flags.state],
-
WMColorPixel(fwin->title_color[fwin->flags.state]), fwin->lbutton_image, False);
+
WMColorPixel(fwin->title_color[fwin->flags.state]), fwin->lbutton_image, False, True);
else
paintButton(button, fwin->title_texture[fwin->flags.state],
-
WMColorPixel(fwin->title_color[fwin->flags.state]), fwin->rbutton_image, False);
+
WMColorPixel(fwin->title_color[fwin->flags.state]), fwin->rbutton_image, False, True);
}
static void titlebarMouseDown(WObjDescriptor * desc, XEvent * event)
@@ -1437,7 +1594,7 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
WCoreWindow *button = desc->self;
WPixmap *image;
XEvent ev;
-
int done = 0, execute = 1;
+
int done = 0, execute = 1, from_xpm = True;
WTexture *texture;
unsigned long pixel;
int clickButton = event->xbutton.button;
@@ -1458,13 +1615,18 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
if (button == fwin->language_button) {
if (!wPreferences.modelock)
return;
-
image = fwin->languagebutton_image;
+
+
/* map focused and pfocused states to focus language button image */
+
int lb_index = wPreferences.new_style == TS_NEW && (fwin->flags.state == 1) ? 1 : 0;
+
+
image = fwin->languagebutton_image[lb_index];
+
from_xpm = False;
}
#endif
pixel = WMColorPixel(fwin->title_color[fwin->flags.state]);
texture = fwin->title_texture[fwin->flags.state];
-
paintButton(button, texture, pixel, image, True);
+
paintButton(button, texture, pixel, image, True, from_xpm);
while (!done) {
WMMaskEvent(dpy, LeaveWindowMask | EnterWindowMask | ButtonReleaseMask
@@ -1472,12 +1634,12 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
switch (ev.type) {
case LeaveNotify:
execute = 0;
-
paintButton(button, texture, pixel, image, False);
+
paintButton(button, texture, pixel, image, False, from_xpm);
break;
case EnterNotify:
execute = 1;
-
paintButton(button, texture, pixel, image, True);
+
paintButton(button, texture, pixel, image, True, from_xpm);
break;
case ButtonPress:
@@ -1492,7 +1654,7 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
WMHandleEvent(&ev);
}
}
-
paintButton(button, texture, pixel, image, False);
+
paintButton(button, texture, pixel, image, False, from_xpm);
if (execute) {
if (button == fwin->left_button) {
diff --git a/src/framewin.h b/src/framewin.h
index 8b0a53ca..1e034755 100644
--- a/src/framewin.h
+++ b/src/framewin.h
@@ -80,7 +80,7 @@ typedef struct WFrameWindow {
WPixmap *lbutton_image;
WPixmap *rbutton_image;
#ifdef XKB_BUTTON_HINT
- WPixmap *languagebutton_image;
+ WPixmap *languagebutton_image[2]; /* focused, unfocused */
#endif
union WTexture **title_texture;
@@ -93,6 +93,7 @@ typedef struct WFrameWindow {
#ifdef KEEP_XKB_LOCK_STATUS
int languagemode;
int last_languagemode;
+ char language_label[3]; /* 2-letter language code */
#endif /* KEEP_XKB_LOCK_STATUS */
/* thing that uses this frame. passed as data to callbacks */
diff --git a/src/screen.c b/src/screen.c
index 009622dd..172a6078 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -658,11 +658,10 @@ WScreen *wScreenInit(int screen_number)
XSelectInput(dpy, scr->root_win, event_mask);
#ifdef KEEP_XKB_LOCK_STATUS
-
/* Only GroupLock doesn't work correctly in my system since right-alt
-
* can change mode while holding it too - ]d
-
*/
if (w_global.xext.xkb.supported)
-
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask|XkbNewKeyboardNotifyMask, XkbIndicatorStateNotifyMask|XkbNewKeyboardNotifyMask);
+
XkbSelectEvents(dpy, XkbUseCoreKbd,
+
XkbIndicatorStateNotifyMask | XkbStateNotifyMask | XkbNewKeyboardNotifyMask,
+
XkbIndicatorStateNotifyMask | XkbStateNotifyMask | XkbNewKeyboardNotifyMask);
#else
if (w_global.xext.xkb.supported)
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask);
diff --git a/src/window.c b/src/window.c
index 71d14a63..032ead46 100644
--- a/src/window.c
+++ b/src/window.c
@@ -24,6 +24,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include <X11/Xatom.h>
#ifdef USE_XSHAPE
#include <X11/extensions/shape.h>
#endif
@@ -38,6 +39,12 @@
#include <string.h>
#include <stdint.h>
#include <math.h>
+#include <ctype.h>
+
+#ifdef XKB_BUTTON_HINT
+#include <X11/extensions/XKBfile.h> // Required for XkbRF_VarDefsRec
+#include <X11/extensions/XKBrules.h> // Required for XkbRF_GetNamesProp
+#endif
/* For getting mouse wheel mappings from WINGs */
#include <WINGs/WINGs.h>
@@ -2304,14 +2311,6 @@ void wWindowUpdateButtonImages(WWindow *wwin)
fwin->lbutton_image = scr->b_pixmaps[WBUT_ICONIFY];
}
}
-#ifdef XKB_BUTTON_HINT
-
if (!WFLAGP(wwin, no_language_button)) {
-
if (fwin->languagebutton_image && !fwin->languagebutton_image->shared)
-
wPixmapDestroy(fwin->languagebutton_image);
-
-
fwin->languagebutton_image = scr->b_pixmaps[WBUT_XKBGROUP1 + fwin->languagemode];
-
}
-#endif
/* close button */
@@ -3145,6 +3144,37 @@ static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event)
}
#ifdef XKB_BUTTON_HINT
+/* Helper function to extract the 2-letter language code for a given XKB group index */
+void wWindowGetLanguageLabel(int group_index, char *label)
+{
+
XkbRF_VarDefsRec vd;
+
/* Default to empty - will fallback to pixmap if we can't get the label */
+
label[0] = '\0';
+
+
if (XkbRF_GetNamesProp(dpy, NULL, &vd) && vd.layout) {
+
int i;
+
char *layout_list = strdup(vd.layout);
+
char *tok = strtok(layout_list, ",");
+
+
/* Iterate to the requested group index */
+
for (i = 0; i < group_index && tok != NULL; i++) {
+
tok = strtok(NULL, ",");
+
}
+
+
if (tok) {
+
/* Copy exactly the first two bytes, then format: first uppercase, second lowercase */
+
strncpy(label, tok, 2);
+
label[2] = '\0';
+
if (label[0])
+
label[0] = (char) toupper((unsigned char) label[0]);
+
if (label[1])
+
label[1] = (char) tolower((unsigned char) label[1]);
+
}
+
+
free(layout_list);
+
}
+}
+
static void windowLanguageClick(WCoreWindow *sender, void *data, XEvent *event)
{
WWindow *wwin = data;
@@ -3158,11 +3188,45 @@ static void windowLanguageClick(WCoreWindow *sender, void *data, XEvent *event)
if (event->xbutton.button != Button1 && event->xbutton.button != Button3)
return;
tl = wwin->frame->languagemode;
-
wwin->frame->languagemode = wwin->frame->last_languagemode;
-
wwin->frame->last_languagemode = tl;
+
+
/* Try to advance to the next available XKB group */
+
XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
+
int newgroup = -1;
+
if (desc && desc->names) {
+
int i;
+
const int MAX_GROUPS = 4; /* typical XKB max groups */
+
for (i = 1; i <= MAX_GROUPS; i++) {
+
int cand = (tl + i) % MAX_GROUPS;
+
Atom a = desc->names->groups[cand];
+
if (a != None) {
+
/* Use XGetAtomName to ensure the atom actually has a name */
+
char *nm = XGetAtomName(dpy, a);
+
if (nm && nm[0] != '\0') {
+
newgroup = cand;
+
XFree(nm);
+
break;
+
}
+
if (nm)
+
XFree(nm);
+
}
+
}
+
}
+
+
if (newgroup >= 0) {
+
wwin->frame->last_languagemode = tl;
+
wwin->frame->languagemode = newgroup;
+
XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
+
} else {
+
/* fallback to previous toggle behaviour for setups with only two
+
* groups or when group info is not available */
+
wwin->frame->languagemode = wwin->frame->last_languagemode;
+
wwin->frame->last_languagemode = tl;
+
XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
+
}
+
/* Update label */
+
wWindowGetLanguageLabel(wwin->frame->languagemode, wwin->frame->language_label);
+
wSetFocusTo(scr, wwin);
-
wwin->frame->languagebutton_image =
-
wwin->frame->screen_ptr->b_pixmaps[WBUT_XKBGROUP1 + wwin->frame->languagemode];
wFrameWindowUpdateLanguageButton(wwin->frame);
if (event->xbutton.button == Button3)
return;
diff --git a/src/window.h b/src/window.h
index 0481b46b..5da5d897 100644
--- a/src/window.h
+++ b/src/window.h
@@ -405,4 +405,9 @@ void wWindowDeleteSavedState(WMagicNumber id);
Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured);
void wWindowSetOmnipresent(WWindow *wwin, Bool flag);
+
+#ifdef XKB_BUTTON_HINT
+void wWindowGetLanguageLabel(int group_index, char *label);
+#endif
+
#endif
--
2.43.0