1
0
mirror of https://github.com/gryf/wmaker.git synced 2026-03-21 02:43:32 +01:00

wmaker: revamp titlebar language button

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).
This commit is contained in:
David Maciejak
2026-03-12 21:16:42 -04:00
committed by Carlos R. Mafra
parent 29177f94ed
commit ec115fedf7
9 changed files with 314 additions and 41 deletions

View File

@@ -555,7 +555,9 @@ AC_ARG_ENABLE([modelock],
m4_divert_pop([INIT_PREPARE])dnl m4_divert_pop([INIT_PREPARE])dnl
AS_IF([test "x$enable_modelock" = "xyes"], 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 dnl XDND Drag-nd-Drop support

View File

@@ -232,3 +232,35 @@ AC_DEFUN_ONCE([WM_XEXT_CHECK_XRANDR],
[supported_xext], [LIBXRANDR], [], [-])dnl [supported_xext], [LIBXRANDR], [], [-])dnl
AC_SUBST([LIBXRANDR])dnl AC_SUBST([LIBXRANDR])dnl
]) dnl AC_DEFUN ]) 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

View File

@@ -165,6 +165,7 @@ wmaker_LDADD = \
@XLFLAGS@ \ @XLFLAGS@ \
@LIBXRANDR@ \ @LIBXRANDR@ \
@LIBXINERAMA@ \ @LIBXINERAMA@ \
@LIBXKBFILE@ \
@XLIBS@ \ @XLIBS@ \
@LIBM@ \ @LIBM@ \
@INTLIBS@ @INTLIBS@

View File

@@ -593,10 +593,12 @@ static void handleExtensions(XEvent * event)
handleShapeNotify(event); handleShapeNotify(event);
} }
#endif #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; XkbEvent *xkbevent = (XkbEvent *) event;
int xkb_type = xkbevent->any.xkb_type;
if (xkbevent->any.xkb_type == XkbNewKeyboardNotify) { if (xkb_type == XkbNewKeyboardNotify) {
int j; int j;
WScreen *scr; WScreen *scr;
@@ -607,8 +609,10 @@ static void handleExtensions(XEvent * event)
} }
#ifdef KEEP_XKB_LOCK_STATUS #ifdef KEEP_XKB_LOCK_STATUS
else { else {
if (wPreferences.modelock && (xkbevent->any.xkb_type == XkbIndicatorStateNotify)) { /* Listen not only for IndicatorStateNotify but also for StateNotify
handleXkbIndicatorStateNotify((XkbEvent *) event); * which is commonly emitted on group (layout) changes. */
if (wPreferences.modelock && (xkb_type == XkbIndicatorStateNotify || xkb_type == XkbStateNotify)) {
handleXkbIndicatorStateNotify(xkbevent);
} }
} }
#endif /*KEEP_XKB_LOCK_STATUS */ #endif /*KEEP_XKB_LOCK_STATUS */
@@ -1324,6 +1328,8 @@ static void handleXkbIndicatorStateNotify(XkbEvent *event)
if (wwin->frame->languagemode != staterec.group) { if (wwin->frame->languagemode != staterec.group) {
wwin->frame->last_languagemode = wwin->frame->languagemode; wwin->frame->last_languagemode = wwin->frame->languagemode;
wwin->frame->languagemode = staterec.group; wwin->frame->languagemode = staterec.group;
wWindowGetLanguageLabel(wwin->frame->languagemode, wwin->frame->language_label);
wFrameWindowUpdateLanguageButton(wwin->frame);
} }
#ifdef XKB_BUTTON_HINT #ifdef XKB_BUTTON_HINT
if (wwin->frame->titlebar) { 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->languagemode = wwin->frame->last_languagemode;
wwin->frame->last_languagemode = staterec.group; wwin->frame->last_languagemode = staterec.group;
XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode); XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
/* Update the language label text */
wWindowGetLanguageLabel(wwin->frame->languagemode, wwin->frame->language_label);
} }
} }
break; break;

View File

@@ -54,7 +54,7 @@ static void resizebarMouseDown(WObjDescriptor * desc, XEvent * event);
static void checkTitleSize(WFrameWindow * fwin); static void checkTitleSize(WFrameWindow * fwin);
static void paintButton(WCoreWindow * button, WTexture * texture, 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); static void updateTitlebar(WFrameWindow * fwin);
@@ -98,6 +98,7 @@ WFrameWindow *wFrameWindowCreate(WScreen * scr, int wlevel, int x, int y,
#ifdef KEEP_XKB_LOCK_STATUS #ifdef KEEP_XKB_LOCK_STATUS
fwin->languagemode = XkbGroup1Index; fwin->languagemode = XkbGroup1Index;
fwin->last_languagemode = XkbGroup2Index; fwin->last_languagemode = XkbGroup2Index;
wWindowGetLanguageLabel(fwin->languagemode, fwin->language_label);
#endif #endif
fwin->depth = depth; fwin->depth = depth;
@@ -145,6 +146,7 @@ void wFrameWindowUpdateBorders(WFrameWindow * fwin, int flags)
theight = *fwin->title_min_height; theight = *fwin->title_min_height;
} else { } else {
theight = 0; theight = 0;
fwin->flags.titlebar = 0;
} }
if (wPreferences.new_style == TS_NEW) { if (wPreferences.new_style == TS_NEW) {
@@ -536,6 +538,8 @@ static void updateTitlebar(WFrameWindow * fwin)
#ifdef XKB_BUTTON_HINT #ifdef XKB_BUTTON_HINT
else { else {
int bsize = theight - 7; 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->flags.hide_left_button || !fwin->left_button || fwin->flags.lbutton_dont_fit) {
if (fwin->language_button) if (fwin->language_button)
wCoreConfigure(fwin->language_button, 3, (theight - bsize) / 2, wCoreConfigure(fwin->language_button, 3, (theight - bsize) / 2,
@@ -950,6 +954,10 @@ void wFrameWindowPaint(WFrameWindow * fwin)
remakeTexture(fwin, i); remakeTexture(fwin, i);
} }
} }
#ifdef XKB_BUTTON_HINT
if (wPreferences.modelock)
wFrameWindowUpdateLanguageButton(fwin);
#endif
} }
if (fwin->flags.need_texture_change) { if (fwin->flags.need_texture_change) {
@@ -1023,7 +1031,8 @@ void wFrameWindowPaint(WFrameWindow * fwin)
allButtons = 0; allButtons = 0;
} }
#ifdef XKB_BUTTON_HINT #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 #endif
if (fwin->title) { if (fwin->title) {
@@ -1228,10 +1237,145 @@ int wFrameWindowChangeTitle(WFrameWindow *fwin, const char *new_title)
} }
#ifdef XKB_BUTTON_HINT #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) void wFrameWindowUpdateLanguageButton(WFrameWindow * fwin)
{ {
paintButton(fwin->language_button, fwin->title_texture[fwin->flags.state], WScreen *scr = fwin->screen_ptr;
WMColorPixel(fwin->title_color[fwin->flags.state]), fwin->languagebutton_image, True); 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 */ #endif /* XKB_BUTTON_HINT */
@@ -1286,7 +1430,7 @@ static void checkTitleSize(WFrameWindow * fwin)
fwin->flags.incomplete_title = 0; 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; WScreen *scr = button->screen_ptr;
GC copy_gc = scr->copy_gc; GC copy_gc = scr->copy_gc;
@@ -1364,8 +1508,12 @@ static void paintButton(WCoreWindow * button, WTexture * texture, unsigned long
} else { } else {
if (wPreferences.new_style == TS_OLD) { if (wPreferences.new_style == TS_OLD) {
XSetForeground(dpy, copy_gc, scr->dark_pixel); XSetForeground(dpy, copy_gc, scr->dark_pixel);
if (from_xpm)
XFillRectangle(dpy, button->window, copy_gc, 0, 0, XFillRectangle(dpy, button->window, copy_gc, 0, 0,
button->width, button->height); button->width, button->height);
else
XCopyArea(dpy, image->image, button->window, copy_gc,
left, 0, width, image->height, x, y);
} else { } else {
XSetForeground(dpy, copy_gc, scr->black_pixel); XSetForeground(dpy, copy_gc, scr->black_pixel);
XCopyArea(dpy, image->image, button->window, copy_gc, 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); XSetForeground(dpy, copy_gc, color);
XSetBackground(dpy, copy_gc, texture->any.color.pixel); XSetBackground(dpy, copy_gc, texture->any.color.pixel);
} }
if (from_xpm)
XFillRectangle(dpy, button->window, copy_gc, 0, 0, button->width, button->height); 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 #ifdef XKB_BUTTON_HINT
if (button == fwin->language_button) { 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], paintButton(button, fwin->title_texture[fwin->flags.state],
WMColorPixel(fwin->title_color[fwin->flags.state]), WMColorPixel(fwin->title_color[fwin->flags.state]),
fwin->languagebutton_image, False); fwin->languagebutton_image[lb_index],
False, False);
}
} else } else
#endif #endif
if (button == fwin->left_button) if (button == fwin->left_button)
paintButton(button, fwin->title_texture[fwin->flags.state], 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 else
paintButton(button, fwin->title_texture[fwin->flags.state], 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) static void titlebarMouseDown(WObjDescriptor * desc, XEvent * event)
@@ -1437,7 +1594,7 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
WCoreWindow *button = desc->self; WCoreWindow *button = desc->self;
WPixmap *image; WPixmap *image;
XEvent ev; XEvent ev;
int done = 0, execute = 1; int done = 0, execute = 1, from_xpm = True;
WTexture *texture; WTexture *texture;
unsigned long pixel; unsigned long pixel;
int clickButton = event->xbutton.button; int clickButton = event->xbutton.button;
@@ -1458,13 +1615,18 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
if (button == fwin->language_button) { if (button == fwin->language_button) {
if (!wPreferences.modelock) if (!wPreferences.modelock)
return; 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 #endif
pixel = WMColorPixel(fwin->title_color[fwin->flags.state]); pixel = WMColorPixel(fwin->title_color[fwin->flags.state]);
texture = fwin->title_texture[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) { while (!done) {
WMMaskEvent(dpy, LeaveWindowMask | EnterWindowMask | ButtonReleaseMask WMMaskEvent(dpy, LeaveWindowMask | EnterWindowMask | ButtonReleaseMask
@@ -1472,12 +1634,12 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
switch (ev.type) { switch (ev.type) {
case LeaveNotify: case LeaveNotify:
execute = 0; execute = 0;
paintButton(button, texture, pixel, image, False); paintButton(button, texture, pixel, image, False, from_xpm);
break; break;
case EnterNotify: case EnterNotify:
execute = 1; execute = 1;
paintButton(button, texture, pixel, image, True); paintButton(button, texture, pixel, image, True, from_xpm);
break; break;
case ButtonPress: case ButtonPress:
@@ -1492,7 +1654,7 @@ static void buttonMouseDown(WObjDescriptor * desc, XEvent * event)
WMHandleEvent(&ev); WMHandleEvent(&ev);
} }
} }
paintButton(button, texture, pixel, image, False); paintButton(button, texture, pixel, image, False, from_xpm);
if (execute) { if (execute) {
if (button == fwin->left_button) { if (button == fwin->left_button) {

View File

@@ -80,7 +80,7 @@ typedef struct WFrameWindow {
WPixmap *lbutton_image; WPixmap *lbutton_image;
WPixmap *rbutton_image; WPixmap *rbutton_image;
#ifdef XKB_BUTTON_HINT #ifdef XKB_BUTTON_HINT
WPixmap *languagebutton_image; WPixmap *languagebutton_image[2]; /* focused, unfocused */
#endif #endif
union WTexture **title_texture; union WTexture **title_texture;
@@ -93,6 +93,7 @@ typedef struct WFrameWindow {
#ifdef KEEP_XKB_LOCK_STATUS #ifdef KEEP_XKB_LOCK_STATUS
int languagemode; int languagemode;
int last_languagemode; int last_languagemode;
char language_label[3]; /* 2-letter language code */
#endif /* KEEP_XKB_LOCK_STATUS */ #endif /* KEEP_XKB_LOCK_STATUS */
/* thing that uses this frame. passed as data to callbacks */ /* thing that uses this frame. passed as data to callbacks */

View File

@@ -658,11 +658,10 @@ WScreen *wScreenInit(int screen_number)
XSelectInput(dpy, scr->root_win, event_mask); XSelectInput(dpy, scr->root_win, event_mask);
#ifdef KEEP_XKB_LOCK_STATUS #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) if (w_global.xext.xkb.supported)
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask|XkbNewKeyboardNotifyMask, XkbIndicatorStateNotifyMask|XkbNewKeyboardNotifyMask); XkbSelectEvents(dpy, XkbUseCoreKbd,
XkbIndicatorStateNotifyMask | XkbStateNotifyMask | XkbNewKeyboardNotifyMask,
XkbIndicatorStateNotifyMask | XkbStateNotifyMask | XkbNewKeyboardNotifyMask);
#else #else
if (w_global.xext.xkb.supported) if (w_global.xext.xkb.supported)
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask); XkbSelectEvents(dpy, XkbUseCoreKbd, XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask);

View File

@@ -24,6 +24,7 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/Xatom.h>
#ifdef USE_XSHAPE #ifdef USE_XSHAPE
#include <X11/extensions/shape.h> #include <X11/extensions/shape.h>
#endif #endif
@@ -38,6 +39,12 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <math.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 */ /* For getting mouse wheel mappings from WINGs */
#include <WINGs/WINGs.h> #include <WINGs/WINGs.h>
@@ -2304,14 +2311,6 @@ void wWindowUpdateButtonImages(WWindow *wwin)
fwin->lbutton_image = scr->b_pixmaps[WBUT_ICONIFY]; 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 */ /* close button */
@@ -3145,6 +3144,37 @@ static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event)
} }
#ifdef XKB_BUTTON_HINT #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) static void windowLanguageClick(WCoreWindow *sender, void *data, XEvent *event)
{ {
WWindow *wwin = data; 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) if (event->xbutton.button != Button1 && event->xbutton.button != Button3)
return; return;
tl = wwin->frame->languagemode; tl = wwin->frame->languagemode;
/* 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->languagemode = wwin->frame->last_languagemode;
wwin->frame->last_languagemode = tl; wwin->frame->last_languagemode = tl;
XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
}
/* Update label */
wWindowGetLanguageLabel(wwin->frame->languagemode, wwin->frame->language_label);
wSetFocusTo(scr, wwin); wSetFocusTo(scr, wwin);
wwin->frame->languagebutton_image =
wwin->frame->screen_ptr->b_pixmaps[WBUT_XKBGROUP1 + wwin->frame->languagemode];
wFrameWindowUpdateLanguageButton(wwin->frame); wFrameWindowUpdateLanguageButton(wwin->frame);
if (event->xbutton.button == Button3) if (event->xbutton.button == Button3)
return; return;

View File

@@ -405,4 +405,9 @@ void wWindowDeleteSavedState(WMagicNumber id);
Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured); Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured);
void wWindowSetOmnipresent(WWindow *wwin, Bool flag); void wWindowSetOmnipresent(WWindow *wwin, Bool flag);
#ifdef XKB_BUTTON_HINT
void wWindowGetLanguageLabel(int group_index, char *label);
#endif
#endif #endif