Open nelk114 opened 4 years ago
I thought it already could use the X input method. @jxy am I imagining that?
I know we support macOS input methods (no completion or emoji from the Touch Bar, nor dictation). I've no idea about X input method.
@rsc @jxy
I worked on XIM support for ACME before and almost finished, but still have some minor issues need to be fix(So no pr now, rough patch attached)
The biggest problem is 'How to get cursor(the text cursor, not mouse cursor) position in devdraw/x11-screen.c and update XIM spot(the im candidate list) to follow the cursor position accordingly."
Such as:
spot.x = cursor_pos_x;
spot.y = cursor_pos_y;
XSetICValues(xic, XNPreeditAttributes, spotlist, NULL);
It seems there is no api to get cursor x/y? Would you mind give me some clue?
Patch:
--- plan9port/src/cmd/devdraw/x11-screen.c
+++ plan9portn/src/cmd/devdraw/x11-screen.c
@@ -10,6 +10,7 @@
#include <mouse.h>
#include <cursor.h>
#include <thread.h>
+#include <locale.h>
#include "x11-memdraw.h"
#include "devdraw.h"
@@ -60,6 +61,12 @@
rpc_flush
};
+static XIC xic;
+static XIM xim;
+static XPoint spot;
+static XVaNestedList spotlist;
+
+
static Xwin*
newxwin(Client *c)
{
@@ -161,6 +168,8 @@
/*
* Connect to X server.
*/
+ setlocale(LC_CTYPE, "");
+ XSetLocaleModifiers("");
_x.display = XOpenDisplay(NULL);
if(_x.display == nil){
disp = getenv("DISPLAY");
@@ -307,6 +316,8 @@
xlock();
while(XPending(_x.display)) {
XNextEvent(_x.display, &event);
+ if (XFilterEvent(&event, None))
+ continue;
runxevent(&event);
}
}
@@ -319,6 +330,9 @@
runxevent(XEvent *xev)
{
int c;
+ char buf[64];
+ Rune rbuf[64];
+ Status status;
KeySym k;
static Mouse m;
XButtonEvent *be;
@@ -364,7 +378,14 @@
case KeyPress:
w = findxwin(((XKeyEvent*)xev)->window);
break;
+ case FocusIn:
+ if (xic)
+ XSetICFocus(xic);
+ w = findxwin(((XFocusChangeEvent*)xev)->window);
+ break;
case FocusOut:
+ if (xic)
+ XUnsetICFocus(xic);
w = findxwin(((XFocusChangeEvent*)xev)->window);
break;
}
@@ -407,8 +428,16 @@
case KeyRelease:
case KeyPress:
ke = (XKeyEvent*)xev;
- XLookupString(ke, NULL, 0, &k, NULL);
+ if (xic && xev->type == KeyPress) {
+ bzero(buf, 64);
+ int len = Xutf8LookupString(xic, ke, buf, sizeof buf, &k, &status);
+ if(len > 0)
+ runesnprint(rbuf, 64, "%s", buf);
+ } else
+ XLookupString(ke, NULL, 0, &k, NULL);
+
c = ke->state;
+
switch(k) {
case XK_Alt_L:
case XK_Meta_L: /* Shift Alt on PCs */
@@ -458,8 +487,20 @@
_xmovewindow(w, w->fullscreen ? w->screenrect : w->windowrect);
return;
}
+
+ if(xic && xev->type == KeyPress) {
+ int nr = runestrlen(rbuf);
+ if (status == XLookupChars && nr > 0 ) {
+ int i = 0;
+ while(i < nr) {
+ gfx_keystroke(w->client, rbuf[i++]);
+ }
+ }
+ }
+
if((c = _xtoplan9kbd(xev)) < 0)
return;
+
gfx_keystroke(w->client, c);
break;
@@ -592,6 +633,17 @@
&attr /* attributes (the above aren't?!) */
);
+ /* input methods */
+ xim = XOpenIM(_x.display, 0, 0, 0);
+ spotlist = XVaCreateNestedList(0, XNSpotLocation, &spot,
+ NULL);
+
+ if (xim)
+ xic = XCreateIC(xim,
+ XNInputStyle, XIMPreeditNothing|XIMStatusNothing,
+ XNClientWindow, w->drawable,
+ NULL);
+
/*
* Label and other properties required by ICCCCM.
*/
@rsc @jxy
Try to support im spot update, I still can not find how to get position of the active text cursor, so update the im spot after string drawn.
Any better way to get the pos of active text cursor?
Update:
As discussed below, try to catch a rectangle with specified fixed width, it should be text cursor, and update IM spot when such a rectangle drawn. dirty but works very well.
By the way, after string drawn, the im spot position still need to update, it's always correct but not enough.
Update:
Add highDPI support.
Update
Fix segfault when imserver quit or wrong XMODIFIERS settings.
iff -Nur plan9port/src/cmd/devdraw/devdraw.c plan9port.cursor/src/cmd/devdraw/devdraw.c
--- plan9port/src/cmd/devdraw/devdraw.c 2020-06-23 22:19:56.000000000 +0800
+++ plan9port.cursor/src/cmd/devdraw/devdraw.c 2020-07-01 21:32:56.324832360 +0800
@@ -803,6 +803,14 @@
if(!dst || !src || !mask)
goto Enodrawimage;
drawrectangle(&r, a+13);
+ /* text cursor width is 3 for DefaultDPI, and will be scaled with highDPI settings.
+ * but a lot of place drawn such a rectangle,
+ * for example, every tag bar had such a text cursor.
+ * So, it's not enough to update im spot here, still need to update it when string drawn
+ */
+ int scale = round((float)client->displaydpi/DefaultDPI);
+ if(Dx(r) == 3 * (scale>1?scale:1))
+ client->impl->rpc_setimposition(client, r.max.x, r.max.y);
drawpoint(&p, a+29);
drawpoint(&q, a+37);
op = drawclientop(client);
@@ -1334,6 +1342,8 @@
}
dst->clipr = clipr;
p.y -= font->ascent;
+ /* update im position after string drawn */
+ client->impl->rpc_setimposition(client, q.x, p.y+Dy(font->image->r));
dstflush(client, dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
continue;
diff -Nur plan9port/src/cmd/devdraw/devdraw.h plan9port.cursor/src/cmd/devdraw/devdraw.h
--- plan9port/src/cmd/devdraw/devdraw.h 2020-06-23 22:19:56.000000000 +0800
+++ plan9port.cursor/src/cmd/devdraw/devdraw.h 2020-07-01 21:28:50.440825335 +0800
@@ -54,6 +54,7 @@
void (*rpc_topwin)(Client*);
void (*rpc_bouncemouse)(Client*, Mouse);
void (*rpc_flush)(Client*, Rectangle);
+ void (*rpc_setimposition)(Client*, int, int);
};
extern QLock drawlk;
diff -Nur plan9port/src/cmd/devdraw/mac-screen.m plan9port.cursor/src/cmd/devdraw/mac-screen.m
--- plan9port/src/cmd/devdraw/mac-screen.m 2020-06-23 22:19:56.000000000 +0800
+++ plan9port.cursor/src/cmd/devdraw/mac-screen.m 2020-07-01 21:28:50.441825335 +0800
@@ -45,6 +45,7 @@
static void rpc_topwin(Client*);
static void rpc_bouncemouse(Client*, Mouse);
static void rpc_flush(Client*, Rectangle);
+static void rpc_setimposition(Client*, int, int);
static ClientImpl macimpl = {
rpc_resizeimg,
@@ -54,7 +55,8 @@
rpc_setmouse,
rpc_topwin,
rpc_bouncemouse,
- rpc_flush
+ rpc_flush,
+ rpc_setimposition
};
@class DrawView;
@@ -478,6 +480,12 @@
}
}
+// Not used in MAC
+static void
+rpc_setimposition(Client *client, int, int)
+{
+}
+
// rpc_flush flushes changes to view.img's rectangle r
// to the on-screen window, making them visible.
// Called from an RPC thread with no client lock held.
diff -Nur plan9port/src/cmd/devdraw/x11-screen.c plan9port.cursor/src/cmd/devdraw/x11-screen.c
--- plan9port/src/cmd/devdraw/x11-screen.c 2020-07-01 21:28:31.275824787 +0800
+++ plan9port.cursor/src/cmd/devdraw/x11-screen.c 2020-07-01 21:28:50.441825335 +0800
@@ -49,6 +49,7 @@
static void rpc_topwin(Client*);
static void rpc_bouncemouse(Client*, Mouse);
static void rpc_flush(Client*, Rectangle);
+static void rpc_setimposition(Client*, int, int);
static ClientImpl x11impl = {
rpc_resizeimg,
@@ -58,7 +59,8 @@
rpc_setmouse,
rpc_topwin,
rpc_bouncemouse,
- rpc_flush
+ rpc_flush,
+ rpc_setimposition
};
static XIC xic;
@@ -1045,6 +1047,16 @@
xunlock();
}
+void
+rpc_setimposition(Client *client, int x, int y)
+{
+ if(xic) {
+ spot.x = x;
+ spot.y = y;
+ XSetICValues(xic, XNPreeditAttributes, spotlist, NULL);
+ }
+}
+
static void
_xexpose(Xwin *w, XEvent *e)
{
cursor position is entirely application level, and I don't think there is an existing mechanism for devdraw to know such things. I used an extra variable in the macOS devdraw to track the screen rectangle being updated. Unfortunately plan9 GUI were not designed with input methods in mind.
cursor position is entirely application level, and I don't think there is an existing mechanism for devdraw to know such things. I used an extra variable in the macOS devdraw to track the screen rectangle being updated. Unfortunately plan9 GUI were not designed with input methods in mind.
@jxy
Thanks, It remind me what I had done in sublime-imfix.c ... Sublime didn't support IM under linux, I used LD_PRELOAD to interpose some gtk functions to insert the IM related codes at runtime, and the cursor (called caret in sublime) width is always 2, then update im position when catching such a rectangle.
//The caret width is 2;
//Maybe sometimes we will make a mistake, but for most of the time, it should be the caret.
if(rectangle->width == 2 && GTK_IS_IM_CONTEXT(local_context)) {
gtk_im_context_set_cursor_location(local_context, rectangle);
}
I tried just now to catch a rectangle with specified width and it's done, actually works very well. But the solution is dirty, I am afraid it will never be able to go to upstream.
Anyway, I will post the patch here for reference. Maybe someone will find a better way in future.
Interesting. Do you know if the width changes in the high DPI mode?
Interesting. Do you know if the width changes in the high DPI mode?
Update: the highDPI scalefactor is very easy to calculate for devdraw/X11, according to '9 man 3 graphics', It's said "Scalesize scales the fixed pixel count n by display->dpi/DefaultDPI, rounding appropriately".
int scale = round((float)client->displaydpi/DefaultDPI);
scale = scale > 1 ? scale : 1;
I updated the second patch above accordingly.
by the way, the man page need update, it mentioned the DefaultDPI is 100, but it already set to 133 in libdraw.
The compose-key sequences (cf.
keyboard(7)
) accepted bymklatinkbd
are limited to accepting ascii characters as input; I'd like to be able to use sequences containing other unicode characters in my (highly customised — though even the GB layout (I have a british keyboard) includes unaccepted non-ascii such as£
) keyboard layout..XCompose
makes this quite trivial but unfortunately leaves me with two quite different sets of compose-key bindings, the plan9port ones both less extensive and less to my liking than the ones I've set up for the rest of my system.Alternatively, if there were some way to have plan9port use the X input method that'd work too.