Skip to content

Commit

Permalink
Support composing characters in the address bar with X input method.
Browse files Browse the repository at this point in the history
  • Loading branch information
gijsbers committed Jun 13, 2024
1 parent 673b8d6 commit 62d5e61
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 22 deletions.
1 change: 1 addition & 0 deletions man/icewm.pod
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ otherwise it is activated by B<KeySysAddressBar>=C<Alt+Ctrl+space>.
In it a shell command can be typed. On I<Enter> it is executed by the
B<AddressBarCommand>=C</bin/sh>. On I<Control+Enter> this command is
executed in a new terminal as given by B<TerminalCommand>.
I<Escape> cancels editing the address bar command.

Commands are executed relative to the working directory of icewm.
This is shown by C<pwd>. Change it with C<cd>. Without argument C<cd>
Expand Down
90 changes: 72 additions & 18 deletions src/yinputline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ YInputLine::YInputLine(YWindow *parent, YInputListener *listener):
fBlinkTime(333),
fKeyPressed(0),
fListener(listener),
inputContext(nullptr),
inputFont(inputFontName),
inputBg(&clrInput),
inputFg(&clrInputText),
Expand All @@ -53,6 +54,8 @@ YInputLine::YInputLine(YWindow *parent, YInputListener *listener):
}

YInputLine::~YInputLine() {
if (inputContext)
XDestroyIC(inputContext);
}

void YInputLine::setText(mstring text, bool asMarked) {
Expand Down Expand Up @@ -322,13 +325,16 @@ bool YInputLine::handleKey(const XKeyEvent &key) {
fListener->inputReturn(this, control);
return true;
}
else
{
char s[16];
else {
const int n = 16;
wchar_t* s = new wchar_t[n];

if (getCharFromEvent(key, s, sizeof(s))) {
replaceSelection(s, strlen(s));
int len = getWCharFromEvent(key, s, n);
if (len) {
replaceSelection(s, len);
return true;
} else {
delete[] s;
}
}
}
Expand All @@ -344,6 +350,18 @@ bool YInputLine::handleKey(const XKeyEvent &key) {
return YWindow::handleKey(key);
}

int YInputLine::getWCharFromEvent(const XKeyEvent& key, wchar_t* s, int maxLen) {
KeySym keysym = None;
Status status = None;
int len = XwcLookupString(inputContext, const_cast<XKeyEvent*>(&key),
s, maxLen, &keysym, &status);

if (inrange(len, 0, maxLen - 1)) {
s[len] = None;
}
return len;
}

void YInputLine::handleButton(const XButtonEvent &button) {
if (button.type == ButtonPress) {
if (button.button == 1) {
Expand Down Expand Up @@ -429,6 +447,9 @@ void YInputLine::handleClick(const XButtonEvent &up, int /*count*/) {
inputMenu->setPopDownListener(this);
} else if (up.button == 2 && xapp->isButton(up.state, Button2Mask)) {
requestSelection(true);
} else if (up.button == 1 && xapp->isButton(up.state, Button1Mask)) {
if (fHasFocus == false)
gotFocus();
}
}

Expand Down Expand Up @@ -466,18 +487,18 @@ unsigned YInputLine::offsetToPos(int offset) {
}

void YInputLine::handleFocus(const XFocusChangeEvent &focus) {
if (focus.mode == NotifyGrab || focus.mode == NotifyUngrab)
return;

if (focus.type == FocusIn &&
focus.detail != NotifyPointer &&
focus.detail != NotifyPointerRoot)
{
fHasFocus = true;
selectAll();
cursorBlinkTimer->setTimer(fBlinkTime, this, true);
gotFocus();
}
else if (focus.type == FocusOut/* && fHasFocus == true*/) {
fHasFocus = false;
repaint();
cursorBlinkTimer = null;
lostFocus();
if (inputMenu && inputMenu == xapp->popup()) {
}
else if (fListener) {
Expand Down Expand Up @@ -566,6 +587,16 @@ void YInputLine::replaceSelection(const char* insert, int amount) {
repaint();
}

void YInputLine::replaceSelection(wchar_t* insert, int amount) {
unsigned from = min(curPos, markPos);
unsigned to = max(curPos, markPos);
YWideString wide(amount, insert);
fText.replace(from, to - from, wide);
curPos = markPos = from + wide.length();
limit();
repaint();
}

bool YInputLine::deleteSelection() {
if (hasSelection()) {
replaceSelection("", 0);
Expand Down Expand Up @@ -849,20 +880,43 @@ bool YInputLine::isFocusTraversable() {
}

void YInputLine::gotFocus() {
if (fHasFocus == false) {
fHasFocus = true;
fCursorVisible = true;
cursorBlinkTimer->setTimer(fBlinkTime, this, true);
fHasFocus = true;
fCursorVisible = true;
cursorBlinkTimer->setTimer(fBlinkTime, this, true);

if (focused() || (YWindow::gotFocus(), focused() == false))
repaint();

if (inputContext == nullptr) {
inputContext =
XCreateIC(xapp->xim(),
XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, handle(),
XNFocusWindow, handle(),
nullptr);
unsigned long mask = None;
XGetICValues(inputContext, XNFilterEvents, &mask, nullptr);
if (mask) {
addEventMask(mask);
}
eventFiltering(true);
}

XSetICFocus(inputContext);
XwcResetIC(inputContext);
}

void YInputLine::lostFocus() {
if (cursorBlinkTimer) {
cursorBlinkTimer = null;
fHasFocus = false;
cursorBlinkTimer = null;
fCursorVisible = false;
fHasFocus = false;
if (focused())
YWindow::lostFocus();
else
repaint();
}

if (inputContext)
XUnsetICFocus(inputContext);
}

// vim: set sw=4 ts=4 et:
3 changes: 3 additions & 0 deletions src/yinputline.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class YInputLine:
bool move(unsigned pos, bool extend);
bool hasSelection() const { return curPos != markPos; }
void replaceSelection(const char* str, int len);
void replaceSelection(wchar_t* str, int len);
bool deleteSelection();
bool deleteNextChar();
bool deletePreviousChar();
Expand All @@ -80,6 +81,7 @@ class YInputLine:
unsigned offsetToPos(int offset);
static mstring completeVariable(mstring var);
static mstring completeUsername(mstring user);
int getWCharFromEvent(const XKeyEvent& key, wchar_t* s, int maxLen);

YWideString fText;
unsigned markPos;
Expand All @@ -93,6 +95,7 @@ class YInputLine:
unsigned fKeyPressed;
YInputListener* fListener;

XIC inputContext;
YFont inputFont;
YColorName inputBg;
YColorName inputFg;
Expand Down
3 changes: 0 additions & 3 deletions src/ylocale.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class YConverter {
iconv_t localer() const { return toLocale; }
const char* localeName() const { return fLocaleName; }
const char* codesetName() const { return fCodeset; }
const char* modifiers() const { return fModifiers; }

private:
void getConverters();
Expand All @@ -47,7 +46,6 @@ class YConverter {
iconv_t toUnicode;
iconv_t toLocale;
const char* fLocaleName;
const char* fModifiers;
const char* fCodeset;
};

Expand All @@ -61,7 +59,6 @@ YConverter::YConverter(const char* localeName) :
"Falling back to 'C' locale'."));
fLocaleName = setlocale(LC_ALL, "C");
}
fModifiers = XSetLocaleModifiers("");
fCodeset = getCodeset();

MSG(("locale: %s, MB_CUR_MAX: %zd, codeset: %s, endian: %c",
Expand Down
7 changes: 6 additions & 1 deletion src/ywindow.cc
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,10 @@ void YWindow::raiseTo(YWindow* inferior) {
}

void YWindow::handleEvent(const XEvent &event) {
if (hasbit(flags, wfFiltering) &&
XFilterEvent(const_cast<XEvent*>(&event), fHandle))
return;

switch (event.type) {
case KeyPress:
case KeyRelease:
Expand Down Expand Up @@ -1863,9 +1867,10 @@ bool YWindow::getCharFromEvent(const XKeyEvent &key, char *s, int maxLen) {
char keyBuf[16];
KeySym ksym;
XKeyEvent kev = key;
static XComposeStatus compose = { NULL, 0 };

// FIXME:
int klen = XLookupString(&kev, keyBuf, sizeof(keyBuf), &ksym, nullptr);
int klen = XLookupString(&kev, keyBuf, sizeof(keyBuf), &ksym, &compose);
#ifndef USE_XmbLookupString
if ((klen == 0) && (ksym < 0x1000)) {
klen = 1;
Expand Down
5 changes: 5 additions & 0 deletions src/ywindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ class YWindow : protected YWindowList, private YWindowNode {

void unmanageWindow() { removeWindow(); }

protected:
void eventFiltering(bool f) { if (f) flags |= wfFiltering;
else flags &= ~wfFiltering; }

private:
enum WindowFlags {
wfVisible = 1 << 0,
Expand All @@ -300,6 +304,7 @@ class YWindow : protected YWindowList, private YWindowNode {
wfToplevel = 1 << 4,
wfNullSize = 1 << 5,
wfFocused = 1 << 6,
wfFiltering = 1 << 7,
};

Window create();
Expand Down
14 changes: 14 additions & 0 deletions src/yxapp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ YXApplication::YXApplication(int *argc, char ***argv, const char *displayName):
lastEventTime(CurrentTime),
fPopup(nullptr),
xfd(this),
fXIM(initInput(fDisplay)),
fXGrabWindow(nullptr),
fGrabWindow(nullptr),
fKeycodeMap(nullptr),
Expand All @@ -1100,6 +1101,17 @@ YXApplication::YXApplication(int *argc, char ***argv, const char *displayName):
initModifiers();
}

XIM YXApplication::initInput(Display* dpy) {
XSetLocaleModifiers("");

XIM xim = XOpenIM(dpy, None, nullptr, nullptr);
if (xim == nullptr) {
XSetLocaleModifiers("@im=none");
xim = XOpenIM(dpy, None, nullptr, nullptr);
}
return xim;
}

void YExtension::init(Display* dis, QueryFunc ext, QueryFunc ver) {
supported = (*ext)(dis, &eventBase, &errorBase)
&& (*ver)(dis, &versionMajor, &versionMinor);
Expand Down Expand Up @@ -1141,6 +1153,8 @@ YXApplication::~YXApplication() {
XFreeColormap(display(), fColormap32);
if (fKeycodeMap)
XFree(fKeycodeMap);
if (fXIM)
XCloseIM(fXIM);

xfd.unregisterPoll();
XCloseDisplay(display());
Expand Down
3 changes: 3 additions & 0 deletions src/yxapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class YXApplication: public YApplication {
void unshift(KeySym* key, unsigned* mod);

static const char* getHelpText();
XIM xim() const { return fXIM; }

protected:
virtual int handleError(XErrorEvent* xev);
Expand Down Expand Up @@ -228,6 +229,7 @@ class YXApplication: public YApplication {
YPopupWindow *fPopup;
friend class YXPoll;
YXPoll xfd;
XIM fXIM;

lazy<class YClipboard> fClip;
YWindow *fXGrabWindow;
Expand All @@ -245,6 +247,7 @@ class YXApplication: public YApplication {
virtual void flushXEvents();

void initModifiers();
static XIM initInput(Display* dpy);
static void initAtoms();

static const char* parseArgs(int argc, char **argv, const char *displayName);
Expand Down

0 comments on commit 62d5e61

Please sign in to comment.