Open Bystroushaak opened 7 years ago
Displaying unicode is very complicated :( The open source world seems to be relying mostly on the Harfbuzz/Pango which aren't simple (!) and which would need quite a bit of work to integrate into Self.
What is your native language? Which characters do you need apart from ASCII?
What is your native language?
Czech language.
Which characters do you need apart from ASCII?
"á", "é", "ě", "í", "ó", "ú", "ů", "ý", "č", "ď", "ň", "ř", "š", "ť", "ž" and "Á", "É", "Ě", "Í", "Ó", "Ú", "Ů", "Ý", "Č", "Ď", "Ň", "Ř", "Š", "Ť", "Ž".
Cool! Self was invented years before Unicode.
On Feb 22, 2017, at 5:48 AM, Bystroushaak notifications@github.com wrote:
What is your native language?
Czech language.
Which characters do you need apart from ASCII?
"á", "é", "ě", "í", "ó", "ú", "ů", "ý", "č", "ď", "ň", "ř", "š", "ť", "ž" and "Á", "É", "Ě", "Í", "Ó", "Ú", "Ů", "Ý", "Č", "Ď", "Ň", "Ř", "Š", "Ť", "Ž".
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.
I've offered to pay ~$500 to one C++ programmer I know to implement Xlib unicode input. He looked at Self and played with it for a while, but then decided, that he can't do it. He sent me this patch file.
I have no idea whether it is useful for something, but posting anyway for documentation purposes and maybe someone will use it.
diff --git a/vm/cmake/dependencies.cmake b/vm/cmake/dependencies.cmake
index 67e8535..7f17ac0 100644
--- a/vm/cmake/dependencies.cmake
+++ b/vm/cmake/dependencies.cmake
@@ -29,10 +29,12 @@ if(SELF_X11)
endif()
set(X11_INCLUDE_DIRS ${X11_INCLUDE_DIR})
endif()
+
+ pkg_check_modules(XFT REQUIRED xft)
- link_directories(${X11_LIBRARY_DIRS})
- include_directories(${X11_INCLUDE_DIRS})
- list(APPEND 3RD_PARTY_LIBS ${X11_LIBRARIES})
+ link_directories(${X11_LIBRARY_DIRS} ${XFT_LIBRARY_DIRS})
+ include_directories(${X11_INCLUDE_DIRS} ${XFT_INCLUDE_DIRS})
+ list(APPEND 3RD_PARTY_LIBS ${X11_LIBRARIES} ${XFT_LIBRARIES})
endif()
if(PKG_CONFIG_FOUND)
diff --git a/vm/src/any/os/xlibWindow.cpp b/vm/src/any/os/xlibWindow.cpp
index 2abf4d2..f23df24 100644
--- a/vm/src/any/os/xlibWindow.cpp
+++ b/vm/src/any/os/xlibWindow.cpp
@@ -16,8 +16,10 @@
XPlatformWindow::XPlatformWindow() : AbstractPlatformWindow() {
- _display = NULL;
- _font_info = NULL;
+ _display = NULL;
+ _gc = NULL;
+ _xft_drawable = NULL;
+ _xft_font = NULL;
}
@@ -27,45 +29,47 @@ bool XPlatformWindow::open( const char* display_name,
int x, int y, int w, int h,
int min_w, int max_w, int min_h, int max_h, // -1 for don't care
const char* window_name, const char* icon_name,
- const char* font_name, int /*font_size unimp X*/ ) {
+ const char* font_name, int font_size) {
// (adapted from Spy open routine, needs font info for sizing)
// XOpenDisplay fails silently if a signal is received during the call.
- // All signals except user interrupts are therefore blocked.
+ // All signals except user interrupts are therefore blocked.
SignalBlocker sb(SignalBlocker::allow_user_int);
-
+
if (!open_xdisplay(display_name)) { close(); return false; }
-
+
bool debugMe = false; // set to true when debugging X
- XSynchronize(_display, debugMe);
+ XSynchronize(_display, debugMe);
XSetErrorHandler(XErrorHandlers::handle_X_error);
_screen_num = DefaultScreen(_display);
+ _colormap = DefaultColormap(_display, _screen_num);
_display_width = DisplayWidth (_display, _screen_num);
_display_height = DisplayHeight(_display, _screen_num);
-
+
_window_x = x; _window_y = y; _width = w; _height = h;
// create the window; will be resized and repositioned when reparented
Window root = RootWindow(_display, _screen_num);
-
- _xwindow = XCreateSimpleWindow( _display, root,
- _window_x, _window_y,
- width(), height(), 0,
- BlackPixel(_display, _screen_num),
- WhitePixel(_display, _screen_num));
-
- if (!set_font_info(font_name)) { close(); return false; }
- if (!change_size_hints(min_w, max_w, min_h, max_h)) { close(); return false; }
-
+
+ _xwindow = XCreateSimpleWindow(_display, root,
+ _window_x, _window_y,
+ width(), height(), 0,
+ BlackPixel(_display, _screen_num),
+ WhitePixel(_display, _screen_num));
+
+ if (!setup_xft_drawable()) { close(); return false; }
+ if (!set_font_info(font_name, font_size)) { close(); return false; }
+ if (!change_size_hints(min_w, max_w, min_h, max_h)) { close(); return false; }
+
if (!set_name( window_name)) { close(); return false; }
if (!set_icon_name( icon_name)) { close(); return false; }
-
- setup_events();
+
+ setup_events();
XMapWindow(_display, _xwindow);
-
+
if (!setup_gcs()) { close(); return false; }
-
+
return true;
}
@@ -82,8 +86,8 @@ bool XPlatformWindow::open_xdisplay(const char *n) {
return true;
}
-
-bool XPlatformWindow::set_icon_name(const char* icon_name) {
+
+bool XPlatformWindow::set_icon_name(const char* icon_name) {
XTextProperty iconName;
if (XStringListToTextProperty((char**)&icon_name, 1, &iconName) == 0) {
warning("X structure allocation for icon name failed--window won't work.");
@@ -105,12 +109,12 @@ bool XPlatformWindow::set_name(const char* window_name) {
}
-void XPlatformWindow::setup_events() {
+void XPlatformWindow::setup_events() {
// to catch the clientMessage event when user deletes
_wmProtocolsAtom = XInternAtom(_display, "WM_PROTOCOLS", false);
_wmDeleteWindowAtom = XInternAtom(_display, "WM_DELETE_WINDOW", false);
XSetWMProtocols(_display, _xwindow, &_wmDeleteWindowAtom, 1);
-
+
// choose events to receive
long event_mask = ExposureMask | StructureNotifyMask;
XSelectInput(_display, _xwindow, event_mask);
@@ -120,7 +124,7 @@ void XPlatformWindow::setup_events() {
bool XPlatformWindow::tell_platform_size_hints() {
if ( _min_w == -1)
return true; // hack for don't care
-
+
// tell window manager that we'd like our own size and position
XSizeHints* size_hints;
if ((size_hints = XAllocSizeHints()) == NULL) {
@@ -138,10 +142,13 @@ bool XPlatformWindow::tell_platform_size_hints() {
}
-bool XPlatformWindow::set_font_info(const char* font_name) {
- // must set _font_info here so that font_height() is defined for BOTTOM
- _font_info = XLoadQueryFont(_display, font_name);
- return _font_info != NULL;
+bool XPlatformWindow::set_font_info(const char* font_name, int size) {
+ // must set _xft_font here so that font_height() is defined for BOTTOM
+ _xft_font = XftFontOpen(_display, _screen_num,
+ XFT_FAMILY, XftTypeString, font_name,
+ XFT_SIZE, XftTypeDouble, double(size),
+ NULL);
+ return _xft_font != NULL;
}
@@ -150,11 +157,10 @@ bool XPlatformWindow::setup_gcs() {
unsigned long valuemask = 0;
XGCValues values;
_gc = XCreateGC(_display, _xwindow, valuemask, &values);
-
- XSetFont(_display, _gc, _font_info->fid);
+
XSetForeground(_display, _gc, BlackPixel(_display, _screen_num));
XSetBackground(_display, _gc, WhitePixel(_display, _screen_num));
-
+
// 16x16 grey stipple pixmap (16x16 is preferred stipple size)
const int grey_width = 16;
const int grey_height = 16;
@@ -165,28 +171,35 @@ bool XPlatformWindow::setup_gcs() {
Pixmap stipple = XCreateBitmapFromData(_display, _xwindow, grey_bits,
grey_width, grey_height);
XSetStipple(_display, _gc, stipple);
-
+
_black= BlackPixel(_display, _screen_num);
_white= WhitePixel(_display, _screen_num);
_is_mono = DefaultDepth(_display, _screen_num) == 1;
if (_is_mono)
_red= _yellow= _gray= _black;
else {
- Colormap cmap= DefaultColormap(_display, _screen_num);
XColor col1, col2;
- _red= XAllocNamedColor(_display, cmap, "red", &col1, &col2)
+ _red= XAllocNamedColor(_display, _colormap, "red", &col1, &col2)
? col1.pixel : _black;
- _yellow= XAllocNamedColor(_display, cmap, "gold", &col1, &col2)
+ _yellow= XAllocNamedColor(_display, _colormap, "gold", &col1, &col2)
? col1.pixel : _black;
- _gray= XAllocNamedColor(_display, cmap, "gray", &col1, &col2)
+ _gray= XAllocNamedColor(_display, _colormap, "gray", &col1, &col2)
? col1.pixel : _black;
}
return true;
}
-void XPlatformWindow::close() {
- if (_font_info != NULL) { XFreeFont(_display, _font_info); _font_info = NULL; }
+void XPlatformWindow::close() {
+ if (_xft_font) {
+ XftFontClose(_display, _xft_font);
+ _xft_font = NULL;
+ }
+ if (_xft_drawable) {
+ XftColorFree(_display, DefaultVisual(_display, _screen_num), _colormap, &_foreground_xft_color);
+ XftDrawDestroy(_xft_drawable);
+ _xft_drawable = NULL;
+ }
if (_gc != NULL) { XFreeGC( _display, _gc); _gc = NULL; }
if (_display != NULL) { XCloseDisplay(_display); _display = NULL; }
}
@@ -213,17 +226,17 @@ int XPlatformWindow::height() { return _height; }
int XPlatformWindow::screen_width() { return _display_width; }
int XPlatformWindow::screen_height() { return _display_height; }
int XPlatformWindow::menubar_height() { return 0; } // none in X
-
-int XPlatformWindow::font_width() { return _font_info->max_bounds.width; }
-int XPlatformWindow::font_height() { return _font_info->max_bounds.ascent + _font_info->max_bounds.descent; }
-const char* XPlatformWindow::default_fixed_font_name() { return "fixed"; }
+int XPlatformWindow::font_width() { return _xft_font->max_advance_width; }
+int XPlatformWindow::font_height() { return _xft_font->height; }
+
+const char* XPlatformWindow::default_fixed_font_name() { return "monospace"; }
int XPlatformWindow::default_fixed_font_size() { return 10; }
// Handy operations;
-bool XPlatformWindow::change_extent(int left, int top, int w, int h) {
+bool XPlatformWindow::change_extent(int left, int top, int w, int h) {
// left, top in global coordinates
// don't need to adjust by insets for X
XMoveResizeWindow(_display, _xwindow, left, top, w, h);
@@ -233,7 +246,7 @@ bool XPlatformWindow::change_extent(int left, int top, int w, int h) {
_height = h;
return true;
}
-void XPlatformWindow::adjust_after_resize() {
+void XPlatformWindow::adjust_after_resize() {
if (TheSpy != NULL)
TheSpy->adjust_after_resize(); // in case this is the spy
}
@@ -255,7 +268,12 @@ bool XPlatformWindow:: pre_draw( bool incremental) {
void XPlatformWindow::post_draw( bool ) { /* XFlush(_display); should be needed but was not there before */ }
void XPlatformWindow::draw_text(const char* text, int x, int y) {
- XDrawImageString(_display, _xwindow, _gc, x, y, text, strlen(text));
+ assert(_xft_drawable);
+ assert(_xft_font);
+
+ XftDrawStringUtf8(_xft_drawable, &_foreground_xft_color,
+ _xft_font, x, y, (FcChar8*)text,
+ strlen(text));
}
void XPlatformWindow::draw_line(int x1, int y1, int x2, int y2) {
@@ -266,7 +284,7 @@ void XPlatformWindow::draw_rectangle_black(int x, int y, int w, int h) {
if (w > 0 && h > 0)
XDrawRectangle(_display, _xwindow, _gc, x, y, w, h);
}
-
+
void XPlatformWindow::clear_rectangle(int x, int y, int w, int h) {
if (w > 0 && h > 0)
@@ -275,23 +293,82 @@ void XPlatformWindow::clear_rectangle(int x, int y, int w, int h) {
// X drawing functions
-// the X calls do the wrong thing if w or h is 0, so suppress these calls
+// the X calls do the wrong thing if w or h is 0, so suppress these calls
void XPlatformWindow::fill_rectangle(int x, int y, int w, int h) {
if (w > 0 && h > 0)
XFillRectangle(_display, _xwindow, _gc, x, y, w, h);
}
-void XPlatformWindow::set_color(int c) { XSetForeground (_display, _gc, c); }
+void XPlatformWindow::set_color(int c) {
+ XSetForeground(_display, _gc, c);
+ set_xft_color_from_pixel(c);
+}
+
void XPlatformWindow::set_thickness(int t) { XSetLineAttributes(_display, _gc, t, LineSolid, CapButt, JoinMiter); }
void XPlatformWindow::set_xor() { XSetFunction (_display, _gc, GXxor); }
void XPlatformWindow::set_copy() { XSetFunction (_display, _gc, GXcopy); }
+bool XPlatformWindow::setup_xft_drawable()
+{
+ _foreground_render_color.red = 0;
+ _foreground_render_color.green = 0;
+ _foreground_render_color.blue = 0;
+ _foreground_render_color.alpha = 65535u;
+
+ if (!XftColorAllocValue(_display,
+ DefaultVisual(_display, _screen_num),
+ _colormap,
+ &_foreground_render_color,
+ &_foreground_xft_color))
+ {
+ return false;
+ }
+
+ _xft_drawable = XftDrawCreate(_display,
+ _xwindow,
+ DefaultVisual(_display, _screen_num),
+ _colormap);
+ if (!_xft_drawable)
+ {
+ XftColorFree(_display, DefaultVisual(_display, _screen_num), _colormap, &_foreground_xft_color);
+ return false;
+ }
-bool XPlatformWindow::get_graphics_semaphore() {
+ return true;
+}
+
+void XPlatformWindow::set_xft_color_from_pixel(unsigned long pixel)
+{
+ XColor xcolor = {0};
+ xcolor.pixel = pixel;
+ xcolor.flags = DoRed | DoGreen | DoBlue;
+ XQueryColor(_display, _colormap, &xcolor);
+
+ XRenderColor renderColor;
+ renderColor.red = xcolor.red;
+ renderColor.green = xcolor.green;
+ renderColor.blue = xcolor.blue;
+ renderColor.alpha = 65535u;
+
+ XftColor xftColor;
+
+ if (XftColorAllocValue(_display,
+ DefaultVisual(_display, _screen_num),
+ _colormap,
+ &renderColor,
+ &xftColor))
+ {
+ XftColorFree(_display, DefaultVisual(_display, _screen_num), _colormap, &_foreground_xft_color);
+ _foreground_render_color = renderColor;
+ _foreground_xft_color = xftColor;
+ }
+}
+
+bool XPlatformWindow::get_graphics_semaphore() {
// if you draw while X may be drawing something else, check this
// and don't draw if it is true
- extern bool xlib_semaphore;
+ extern bool xlib_semaphore;
return xlib_semaphore;
}
@@ -306,22 +383,22 @@ bool XPlatformWindow::handle_polled_events() {
while ( XPending(_display) > 0 ) {
XNextEvent(_display, &event);
switch (event.type) {
-
+
case Expose:
if (event.xexpose.count != 0) break;
full_redraw(); // force redraw
break;
-
+
case ConfigureNotify:
_width = event.xconfigure.width;
_height = event.xconfigure.height;
adjust_after_resize();
break;
-
- case ReparentNotify:
+
+ case ReparentNotify:
handle_reparent_event(event);
break;
-
+
case ClientMessage:
if ((event.xclient.message_type = _wmProtocolsAtom)
&& (event.xclient.data.l[0] = _wmDeleteWindowAtom)) {
@@ -330,7 +407,7 @@ bool XPlatformWindow::handle_polled_events() {
return false;
}
break;
-
+
default:
break;
}
@@ -344,13 +421,13 @@ void XPlatformWindow::handle_reparent_event(XEvent& event) {
int x, y;
unsigned w, h, border_width, depth;
if (XGetGeometry(_display, event.xreparent.parent, &root, &x, &y,
- &w, &h, &border_width, &depth) == 0)
+ &w, &h, &border_width, &depth) == 0)
return;
int wdelta = w - width();
// sanity check: sometimes X gives weird width
if (wdelta < 0) {
if (XGetGeometry(_display, event.xreparent.parent, &root, &x, &y,
- &w, &h, &border_width, &depth) == 0)
+ &w, &h, &border_width, &depth) == 0)
return;
wdelta = w - width();
if (wdelta < 0) {
diff --git a/vm/src/any/os/xlibWindow.hh b/vm/src/any/os/xlibWindow.hh
index 396bf5a..a6c43d0 100644
--- a/vm/src/any/os/xlibWindow.hh
+++ b/vm/src/any/os/xlibWindow.hh
@@ -20,11 +20,15 @@ class XPlatformWindow: public AbstractPlatformWindow {
int _screen_num;
int _display_width, _display_height;
Window _xwindow;
+ XftDraw* _xft_drawable;
+ Colormap _colormap;
int _window_x, _window_y;
- int _width, _height;
- XFontStruct* _font_info;
+ int _width, _height;
+ XftFont* _xft_font;
Atom _wmProtocolsAtom, _wmDeleteWindowAtom;
GC _gc;
+ XRenderColor _foreground_render_color;
+ XftColor _foreground_xft_color;
bool _is_mono;
public:
@@ -90,11 +94,14 @@ class XPlatformWindow: public AbstractPlatformWindow {
// Open helpers
bool open_xdisplay(const char*);
void setup_events();
+ bool setup_xft_drawable();
bool set_name(const char*);
bool set_icon_name(const char*);
- bool set_font_info(const char*);
+ bool set_font_info(const char*, int size);
bool setup_gcs();
+ void set_xft_color_from_pixel(unsigned long pixel);
+
// Drawing helpers
bool get_graphics_semaphore(); // X is not reentrant
diff --git a/vm/src/unix/prims/x_includes.hh b/vm/src/unix/prims/x_includes.hh
index 9349ee8..06db282 100644
--- a/vm/src/unix/prims/x_includes.hh
+++ b/vm/src/unix/prims/x_includes.hh
@@ -12,6 +12,7 @@
# define Cursor SelfX11Cursor // prevent clash with Carbon
# include <X11/Xlib.h>
# include <X11/Xutil.h>
+# include <X11/Xft/Xft.h>
# undef Cursor
# if TARGET_OS_VERSION == MACOSX_VERSION
# undef Status
Back when I looked at Linux fonts and Xft I think I tried XPlatformWindow too, but it wasn't where the font code was being done. It's done in Self. I wrote a post on how to do Xft fonts from Self with the code in this branch. It adds the primitives to enable displaying fonts but doesn't hook it into the existing font code. You'd probably need to follow how the Mac OS Self code does this as it also uses a different font mechanism.
I will have to look how is input handled. I suspect, that there may be just some condition that prevents input of any character that self doesn't understand. In the terminal, everything works as expected:
"Self 1" 'č'
'\xc4\x8d'
"Self 5" 'ç' printLine ç '\xc3\xa7'
printing to console works too.
To do this right shouldn't we transition to a utf8 string type - detach traits string
from traits byteVector
and make it standalone to handle multibyte characters etc?
To do this right shouldn't we transition to a utf8 string type - detach traits string from traits byteVector and make it standalone to handle multibyte characters etc?
That would be ideal, but I think that input and output has higher priority.
UTF API support is nice thing to have, but you can work around it, if you don't (for example - search for substring, instead of single character). Missing UTF input means, that you literally can't type into the application you are trying to build, which makes it unusable.
I was again looking into the X wrappers yesterday, but the more I see, the less I understand how it actually works. I was talking about it with few C++/Unix programmers, but they all run away when they've seen primitive maker and how the bindings are connected :disappointed:
I've managed to trace the input into the traits abstractUI2Event editorKeyCapComboHandler
, where is code for handling the input like handlePressWithNoModifiers: combo IfCannot: b
, which is limited only to printable characters by this line:
combo nonmodifierKeyCap isPrintable ifTrue: [^combo nonmodifierKeyCap printString do: [|:c| insert_char: c]].
isPrintable
is message from string and it just checks whether the character is > 32 && < 128, which limits the input for characters with higher code. When I allowed higher codes (by changing isPrintable implementation), it is now possible to enter ýáíéú
(and they also display correctly!), but not ěščřžů
or any combining keys (' + a = á
, ˇ + d = ď
and so on). I am not yet sure why. What I find really strange that using autocutsel, I can copy the written characters out of Self, but when I try to paste them back, garbage occurs.
I am not sure, how is handlePressWithNoModifiers: combo IfCannot: b
connected with general Self input and whether this code is just for the editor, or all input. Probably for all input from what I can read from sendMessageToHandleKeyboardEventTo:
.
I will need to play with the input system a little bit more to understand how it would be possible to allow any input.
Note to my future self: Is it possible to define combo
to include unicode character?
I've played with Self again today and managed to trace the input handling into the:
traits abstractUI2Event editorKeyCapComboHandler
where is handlePressWithNoModifiers: combo IfCannot: b
This may be updated to accept any character input by changing:
combo nonmodifierKeyCap isPrintable ifTrue: [^combo nonmodifierKeyCap printString do: [|:c| insert_char: c]].
to
combo nonmodifierKeyCap printString size <= 6 ifTrue: [^combo nonmodifierKeyCap printString do: [|:c| insert_char: c]].
That is the easier part, which will allow input such as éíáý (ISO 8859-1 btw), as I discovered yesterday.
The harder part is keyboard event handling in traits ui2XEvent
, specifically keySymFrom: xEvt
, which is defined as:
| k |
k: xEvt lookupKeySym.
k = xEvt xk_Left ifTrue: [ ^ keyCaps arrows left ].
k = xEvt xk_Up ifTrue: [ ^ keyCaps arrows up ].
k = xEvt xk_Right ifTrue: [ ^ keyCaps arrows right ].
k = xEvt xk_Down ifTrue: [ ^ keyCaps arrows down ].
k = xEvt xk_KP_Left ifTrue: [ ^ keyCaps arrows left ].
k = xEvt xk_KP_Up ifTrue: [ ^ keyCaps arrows up ].
k = xEvt xk_KP_Right ifTrue: [ ^ keyCaps arrows right ].
k = xEvt xk_KP_Down ifTrue: [ ^ keyCaps arrows down ].
k = xEvt xk_Shift_L ifTrue: [ ^ keyCaps oddballs shift ].
k = xEvt xk_Shift_R ifTrue: [ ^ keyCaps oddballs shift ].
k = xEvt xk_Control_L ifTrue: [ ^ keyCaps oddballs control ].
k = xEvt xk_Control_R ifTrue: [ ^ keyCaps oddballs control ].
k = xEvt xk_Alt_L ifTrue: [ ^ keyCaps oddballs alt ].
k = xEvt xk_Alt_R ifTrue: [ ^ keyCaps oddballs alt ].
k = xEvt xk_Super_L ifTrue: [ ^ keyCaps oddballs command ].
k = xEvt xk_Super_R ifTrue: [ ^ keyCaps oddballs command ].
keyCaps unknown
This is translator from unrecognized key codes to appropriate characters / strings.
By inserting
k = 488 ifTrue: [^ keyCaps printableCharacter copy character: '\xa9'].
before keyCaps unknown
, I was able to map č
key (keysym 0x1e8, =488) to '\xa9'
ISO 8859-1 character (© mark). It is also possible to enter unicode strings such as '\xc4\x8d'
, but they will of course display itself as garbage (Ä⬚
), because they are interpreted as ISO 8859-1.
Important point is, that translation from keysym to strings may be done here. I've tried xEvt lookupString
, which may be used to resolve some of the other keys, but it returns empty strings for keys like č
.
Now, the question is whether there is any possibility for mapping the keysyms (or keycode (=13), both are in xEvt object) to proper unicode representations automatically by calling some X function and half of the work is done.
I was thinking about implementing translation table for keysyms in Self, but decided I will try to do it in C++ Xlib code, because I don't want to replicate functionality, which is already there.
I am able to get UTF-8 input for almost* all keys, by modification of https://github.com/russellallen/self/blob/master/objects/glue/xlib_glue.cpp#L448:
from:
int XLookupString_wrap(XKeyEvent* event, char* string, int len,
objVectorOop keySym) {
KeySym ks;
int n = XLookupString(event, string, len, &ks, NULL);
if (keySym->length() >= 1) keySym->obj_at_put(0, as_smiOop(ks), false);
return n;
}
to:
XIC getXICContext(XKeyEvent* event){
XIM im = XOpenIM(event->display, NULL, NULL, NULL);
XIC ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, event->window, NULL);
XSetICFocus(ic);
return ic;
}
int XLookupString_wrap(XKeyEvent* event, char* string, int len,
objVectorOop keySym) {
KeySym ks;
int n = Xutf8LookupString(getXICContext(event), event, string, len, &ks, NULL);
if (keySym->length() >= 1)
keySym->obj_at_put(0, as_smiOop(ks), false);
return n;
}
It is basically just using Xutf8LookupString instead of XLookupString
. It works nicely for keys like á
.
*Except combining / dead keys (´
+ a
= á
). They should somehow work with XFilterEvent, but I wasn't able to make it work. I've tried to modify XNextEvent_wrap from:
oop XNextEvent_wrap(Display *display, bool peek,
objVectorOop eventProtos, void *FH) {
XEvent *evt = new XEvent();
if (peek)
XPeekEvent(display, evt);
else
XNextEvent(display, evt);
int type = evt->type;
if (type < 0 || type >= eventProtos->length()) {
char err[50];
sprintf(err, "unknown X event, type = %d", type);
failure(FH, err);
delete evt;
return NULL;
}
oop proto = eventProtos->obj_at(type);
if (!proto->is_proxy()) {
prim_failure(FH, BADTYPEERROR);
delete evt;
return NULL;
}
proxyOop res = proxyOop(proto)->clone();
res->set_pointer(evt);
res->set_type_seal(XEvent_seal);
return res;
}
to:
oop XNextEvent_wrap(Display *display, bool peek,
objVectorOop eventProtos, void *FH) {
XEvent *evt = new XEvent();
if (peek)
XPeekEvent(display, evt);
else
XNextEvent(display, evt);
if (!peek){
int revert_to;
Window active_win;
XGetInputFocus(display, &active_win, &revert_to);
if (XFilterEvent(evt, active_win)){
//delete evt;
return NULL;
}
}
int type = evt->type;
if (type < 0 || type >= eventProtos->length()) {
char err[50];
sprintf(err, "unknown X event, type = %d", type);
failure(FH, err);
delete evt;
return NULL;
}
oop proto = eventProtos->obj_at(type);
if (!proto->is_proxy()) {
prim_failure(FH, BADTYPEERROR);
delete evt;
return NULL;
}
proxyOop res = proxyOop(proto)->clone();
res->set_pointer(evt);
res->set_type_seal(XEvent_seal);
return res;
}
Which generates 0
event, which I fixed by ignoring such events. But still, it doesn't work. When I press the dead key for ´
and then quickly some other key (a
for example), it generates garbage, which changes randomly. If I press other key after a while, it just adds the other key.
From „debug printouts“, I can say that it looks like XLookupString_wrap()
is called multiple times, or maybe ´
event is still registered, even if it should be ignored.
I don't really understand how Xlib / Self wrapper works, so maybe I am doing something obviously wrong.
Could it be related to the peek event handling? Maybe filter on peek too?
I've tried that, but nope, not related. I am kinda confused by that random output, which looks like it maybe reads pieces of memory which it shouldn't.
I've made some progress and it is almost working now, except when the combining / dead key is pressed with shift.
So, now when the input works, I am still fighting with output. I didn't put much time into it lately, but I am making slow progress.
I've added new primitive Xutf8DrawString_wrap
using primitive maker, which uses Xutf8DrawString
instead of XDrawString
used so far. Problem is, that Xutf8DrawString
is defined as:
void Xutf8DrawString(Display *display, Drawable d, XFontSet font_set, GC gc, int x, int y, char *string, int num_bytes);
and XDrawString
as
int XDrawString(Display *display, Drawable d, GC gc, int x, int y, char *string, int length);
Notice, that Xutf8DrawString
is using extra parameter XFontSet font_set
. This means, that font is set differently and needs to be passed with each call, which means that I have to add XFontSet
structure to Self using primitive maker and also change the Self code which handles setting of font.
Cool! Nice to see this happening.
On Jul 7, 2017, at 11:05 AM, Bystroushaak notifications@github.com wrote:
So, now when the input works, I am still fighting with output. I didn't put much time into it lately, but I am making slow progress.
I've added new primitive Xutf8DrawString_wrap using primitive maker, which uses Xutf8DrawString instead of XDrawString used so far. Problem is, that Xutf8DrawString is defined as:
void Xutf8DrawString(Display display, Drawable d, XFontSet font_set, GC gc, int x, int y, char string, int num_bytes); and XDrawString as
int XDrawString(Display display, Drawable d, GC gc, int x, int y, char string, int length); Notice, that Xutf8DrawString is using extra parameter XFontSet font_set. This means, that font is set differently and needs to be passed with each call, which means that I have to add XFontSet structure to Self using primitive maker and also change the Self code which handles font's.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/russellallen/self/issues/113#issuecomment-313753285, or mute the thread https://github.com/notifications/unsubscribe-auth/AAcJ0NmbcQIKy5Ix9ixWhTq0U8-COc2rks5sLnN8gaJpZM4MGJv7.
It works as proof of concept as the hack on C++ level. It looks horribly, because of the different unicode fallback font, and aligns strings wrongly, because it still uses XFontStruct
to compute width, instead of XFontSet
. But it works.
Now I will have to port code to Self and also rewrite Xlib methods for querying the width of string and such to use XFontSet
.
This is nice! Are you still using the original bitmap X font system or the (less ancient) xft?
So far just the original bitmap system. I will look into xft.
Non-ASCII unicode characters ("č" for example) can't be written in the graphic interface. When I press the "č" button on my keyboard, nothing happens.
Also if I read unicode file, it is impossible to show non-ASCII output (garbage occurs instead).
I am aware, that full unicode support in the API would be hard, but input / output should be possible. Right now, it is impossible for me to create usable application in Self, because I can't type into it in my native language.