[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/exwm 0f7269c 5/7: Add input method support
From: |
Chris Feng |
Subject: |
[elpa] externals/exwm 0f7269c 5/7: Add input method support |
Date: |
Wed, 6 Feb 2019 06:35:33 -0500 (EST) |
branch: externals/exwm
commit 0f7269c4ec666eb8bcf2616abbb5af46087cbdea
Author: Chris Feng <address@hidden>
Commit: Chris Feng <address@hidden>
Add input method support
; The code is basically refactored from
; https://github.com/ch11ng/exim to get better maintenance.
* exwm-xim.el: New module making Emacs's builtin input methods usable
for interacting with X windows.
* exwm-core.el (exwm--intern-atom): New function for intern X11 atoms.
* exwm-input.el (exwm-input--init):
* exwm-manage.el (exwm-manage--init): Use it.
---
README.md | 3 +-
exwm-core.el | 9 +
exwm-input.el | 15 +-
exwm-manage.el | 9 +-
exwm-randr.el | 11 +-
exwm-systemtray.el | 7 +-
exwm-xim.el | 781 +++++++++++++++++++++++++++++++++++++++++++++++++++++
xinitrc | 19 +-
8 files changed, 812 insertions(+), 42 deletions(-)
diff --git a/README.md b/README.md
index 103948c..6d7e0dd 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,8 @@ It features:
+ Dynamic workspace support
+ ICCCM/EWMH compliance
+ (Optional) RandR (multi-monitor) support
-+ (Optional) Built-in system tray
++ (Optional) Builtin system tray
++ (Optional) Builtin input method
Please check out the
[screenshots](https://github.com/ch11ng/exwm/wiki/Screenshots)
diff --git a/exwm-core.el b/exwm-core.el
index e13a319..9b6877b 100644
--- a/exwm-core.el
+++ b/exwm-core.el
@@ -131,6 +131,15 @@ Nil can be passed as placeholder."
(if height xcb:ConfigWindow:Height 0))
:x x :y y :width width :height height)))
+(defun exwm--intern-atom (atom)
+ "Intern X11 ATOM."
+ (slot-value (xcb:+request-unchecked+reply exwm--connection
+ (make-instance 'xcb:InternAtom
+ :only-if-exists 0
+ :name-len (length atom)
+ :name atom))
+ 'atom))
+
(defmacro exwm--defer (secs function &rest args)
"Defer the execution of FUNCTION.
diff --git a/exwm-input.el b/exwm-input.el
index a44bd93..4f64d35 100644
--- a/exwm-input.el
+++ b/exwm-input.el
@@ -542,9 +542,9 @@ instead."
(exwm-input--update-global-prefix-keys)))
;; Putting (t . EVENT) into `unread-command-events' does not really work
-;; as documented for Emacs < 27.
+;; as documented for Emacs < 26.2.
(eval-and-compile
- (if (< emacs-major-version 27)
+ (if (string-version-lessp emacs-version "26.2")
(defsubst exwm-input--unread-event (event)
(setq unread-command-events
(append unread-command-events (list event))))
@@ -909,7 +909,7 @@ Notes:
* Setting the value directly (rather than customizing it) after EXWM
finishes initialization has no effect.
* Original-keys consist of multiple key events are only supported in Emacs
- 27 and later.
+ 26.2 and later.
* A minority of applications do not accept simulated keys by default. It's
required to customize them to accept events sent by SendEvent.
* The predefined examples in the Customize interface are not guaranteed to
@@ -1035,14 +1035,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key
sequences."
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window exwm-input--timestamp-window
:data "EXWM: exwm-input--timestamp-window"))
- (let ((atom "_TIME"))
- (setq exwm-input--timestamp-atom
- (slot-value (xcb:+request-unchecked+reply exwm--connection
- (make-instance 'xcb:InternAtom
- :only-if-exists 0
- :name-len (length atom)
- :name atom))
- 'atom)))
+ (setq exwm-input--timestamp-atom (exwm--intern-atom "_TIME"))
;; Initialize global keys.
(dolist (i exwm-input-global-keys)
(exwm-input--set-key (car i) (cdr i)))
diff --git a/exwm-manage.el b/exwm-manage.el
index 759ec0b..b41512c 100644
--- a/exwm-manage.el
+++ b/exwm-manage.el
@@ -715,14 +715,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
"Initialize manage module."
;; Intern _MOTIF_WM_HINTS
(exwm--log)
- (let ((atom-name "_MOTIF_WM_HINTS"))
- (setq exwm-manage--_MOTIF_WM_HINTS
- (slot-value (xcb:+request-unchecked+reply exwm--connection
- (make-instance 'xcb:InternAtom
- :only-if-exists 0
- :name-len (length atom-name)
- :name atom-name))
- 'atom)))
+ (setq exwm-manage--_MOTIF_WM_HINTS (exwm--intern-atom "_MOTIF_WM_HINTS"))
(add-hook 'after-make-frame-functions #'exwm-manage--add-frame)
(add-hook 'delete-frame-functions #'exwm-manage--remove-frame)
(xcb:+event exwm--connection 'xcb:ConfigureRequest
diff --git a/exwm-randr.el b/exwm-randr.el
index 4338152..7d20022 100644
--- a/exwm-randr.el
+++ b/exwm-randr.el
@@ -48,7 +48,9 @@
;;; Code:
(require 'xcb-randr)
+
(require 'exwm-core)
+(require 'exwm-workspace)
(defgroup exwm-randr nil
"RandR."
@@ -93,17 +95,8 @@ corresponding monitors whenever the monitors are active.
(defvar exwm-randr--last-timestamp 0 "Used for debouncing events.")
-(defvar exwm-workspace--fullscreen-frame-count)
-(defvar exwm-workspace--list)
(defvar exwm-randr--prev-screen-change-seqnum nil
"The most recent ScreenChangeNotify sequence number.")
-(declare-function exwm-workspace--count "exwm-workspace.el")
-(declare-function exwm-workspace--set-active "exwm-workspace.el"
- (frame active))
-(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ())
-(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
-(declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ())
-(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
(defun exwm-randr--get-monitors ()
"Get RandR monitors."
diff --git a/exwm-systemtray.el b/exwm-systemtray.el
index 0ff4efc..80505c2 100644
--- a/exwm-systemtray.el
+++ b/exwm-systemtray.el
@@ -33,7 +33,9 @@
(require 'xcb-icccm)
(require 'xcb-xembed)
(require 'xcb-systemtray)
+
(require 'exwm-core)
+(require 'exwm-workspace)
(defclass exwm-systemtray--icon ()
((width :initarg :width)
@@ -77,12 +79,7 @@ You shall use the default value if using auto-hide
minibuffer."
(defvar exwm-systemtray--selection-owner-window nil
"The selection owner window.")
-(defvar exwm-workspace--current)
-(defvar exwm-workspace--minibuffer)
-(defvar exwm-workspace--workareas)
-(defvar exwm-workspace-current-index)
(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
-(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
(defun exwm-systemtray--embed (icon)
"Embed an icon."
diff --git a/exwm-xim.el b/exwm-xim.el
new file mode 100644
index 0000000..6a213ac
--- /dev/null
+++ b/exwm-xim.el
@@ -0,0 +1,781 @@
+;;; exwm-xim.el --- XIM Module for EXWM -*- lexical-binding: t -*-
+
+;; Copyright (C) 2019 Free Software Foundation, Inc.
+
+;; Author: Chris Feng <address@hidden>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This module adds XIM support for EXWM and allows sending characters
+;; generated by any Emacs's builtin input method (info node `Input Methods')
+;; to X windows.
+
+;; This module is essentially an X input method server utilizing Emacs as
+;; its backend. It talks with X windows through the XIM protocol. The XIM
+;; protocol is quite flexible by itself, stating that an implementation can
+;; create network connections of various types as well as make use of an
+;; existing X connection for communication, and that an IM server may
+;; support multiple transport versions, various input styles and several
+;; event flow modals, etc. Here we only make choices that are most popular
+;; among other IM servers and more importantly, practical for Emacs to act
+;; as an IM server:
+;;
+;; + Packets are transported on top of an X connection like most IMEs.
+;; + Only transport version 0.0 (i.e. only-CM & Property-with-CM) is
+;; supported (same as "IM Server Developers Kit", adopted by most IMEs).
+;; + Only support static event flow, on-demand-synchronous method.
+;; + Only "root-window" input style is supported.
+
+;; To use this module, first load and enable it as follows:
+;;
+;; (require 'exwm-xim)
+;; (exwm-xim-enable)
+;;
+;; A keybinding for `toggle-input-method' is probably required to turn on &
+;; off an input method (default to `default-input-method'). It's bound to
+;; 'C-\' by default and can be made reachable when working with X windows:
+;;
+;; (push ?\C-\\ exwm-input-prefix-keys)
+;;
+;; It's also required (and error-prone) to setup environment variables to
+;; make applications actually use this input method. Typically the
+;; following lines should be inserted into '~/.xinitrc'.
+;;
+;; export address@hidden
+;; export GTK_IM_MODULE=xim
+;; export QT_IM_MODULE=xim
+;; export CLUTTER_IM_MODULE=xim
+
+;; References:
+;; + XIM (http://www.x.org/releases/X11R7.6/doc/libX11/specs/XIM/xim.html)
+;; + IMdkit (http://xorg.freedesktop.org/archive/unsupported/lib/IMdkit/)
+;; + UIM (https://github.com/uim/uim)
+
+;;; Code:
+
+(eval-when-compile (require 'cl-lib))
+
+(require 'xcb-keysyms)
+(require 'xcb-xim)
+
+(require 'exwm-core)
+(require 'exwm-input)
+
+(defconst exwm-xim--locales
+ "@locale=\
+aa,af,ak,am,an,anp,ar,as,ast,ayc,az,be,bem,ber,bg,bhb,bho,bn,bo,br,brx,bs,byn,\
+ca,ce,cmn,crh,cs,csb,cv,cy,da,de,doi,dv,dz,el,en,es,et,eu,fa,ff,fi,fil,fo,fr,\
+fur,fy,ga,gd,gez,gl,gu,gv,ha,hak,he,hi,hne,hr,hsb,ht,hu,hy,ia,id,ig,ik,is,it,\
+iu,iw,ja,ka,kk,kl,km,kn,ko,kok,ks,ku,kw,ky,lb,lg,li,li,lij,lo,lt,lv,lzh,mag,\
+mai,mg,mhr,mi,mk,ml,mn,mni,mr,ms,mt,my,nan,nb,nds,ne,nhn,niu,nl,nn,nr,nso,oc,\
+om,or,os,pa,pa,pap,pl,ps,pt,quz,raj,ro,ru,rw,sa,sat,sc,sd,se,shs,si,sid,sk,sl,\
+so,sq,sr,ss,st,sv,sw,szl,ta,tcy,te,tg,th,the,ti,tig,tk,tl,tn,tr,ts,tt,ug,uk,\
+unm,ur,uz,ve,vi,wa,wae,wal,wo,xh,yi,yo,yue,zh,zu,\
+C,no"
+ "All supported locales (stolen from glibc).")
+
+(defconst exwm-xim--default-error
+ (make-instance 'xim:error
+ :im-id 0
+ :ic-id 0
+ :flag xim:error-flag:invalid-both
+ :error-code xim:error-code:bad-something
+ :length 0
+ :type 0
+ :detail nil)
+ "Default error returned to clients.")
+
+(defconst exwm-xim--default-im-attrs
+ (list (make-instance 'xim:XIMATTR
+ :id 0
+ :type xim:ATTRIBUTE-VALUE-TYPE:xim-styles
+ :length (length xlib:XNQueryInputStyle)
+ :attribute xlib:XNQueryInputStyle))
+ "Default IM attrs returned to clients.")
+
+(defconst exwm-xim--default-ic-attrs
+ (list (make-instance 'xim:XICATTR
+ :id 0
+ :type xim:ATTRIBUTE-VALUE-TYPE:long-data
+ :length (length xlib:XNInputStyle)
+ :attribute xlib:XNInputStyle)
+ (make-instance 'xim:XICATTR
+ :id 1
+ :type xim:ATTRIBUTE-VALUE-TYPE:window
+ :length (length xlib:XNClientWindow)
+ :attribute xlib:XNClientWindow)
+ ;; Required by e.g. xterm.
+ (make-instance 'xim:XICATTR
+ :id 2
+ :type xim:ATTRIBUTE-VALUE-TYPE:window
+ :length (length xlib:XNFocusWindow)
+ :attribute xlib:XNFocusWindow))
+ "Default IC attrs returned to clients.")
+
+(defconst exwm-xim--default-styles
+ (make-instance 'xim:XIMStyles
+ :number nil
+ :styles (list (logior xlib:XIMPreeditNothing
+ xlib:XIMStatusNothing)))
+ "Default styles: root-window, i.e. no preediting or status display support.")
+
+(defconst exwm-xim--default-attributes
+ (list (make-instance 'xim:XIMATTRIBUTE
+ :id 0
+ :length nil
+ :value exwm-xim--default-styles))
+ "Default IM/IC attributes returned to clients.")
+
+(defvar exwm-xim--conn nil
+ "The X connection for initiating other XIM connections.")
+(defvar exwm-xim--event-xwin nil
+ "X window for initiating new XIM connections.")
+(defvar exwm-xim--server-client-plist '(nil nil)
+ "Plist mapping server window to [X connection, client window, byte-order].")
+(defvar exwm-xim--client-server-plist '(nil nil)
+ "Plist mapping client window to server window.")
+(defvar exwm-xim--property-index 0 "For generating a unique property name.")
+(defvar exwm-xim--im-id 0 "Last IM ID.")
+(defvar exwm-xim--ic-id 0 "Last IC ID.")
+(defvar exwm-xim--event-pending nil
+ "Indicating whether Emacs requires more events.")
+
+;; X11 atoms.
+(defvar address@hidden nil)
+(defvar exwm-xim--LOCALES nil)
+(defvar exwm-xim--TRANSPORT nil)
+(defvar exwm-xim--XIM_SERVERS nil)
+(defvar exwm-xim--_XIM_PROTOCOL nil)
+(defvar exwm-xim--_XIM_XCONNECT nil)
+
+(defun exwm-xim--on-SelectionRequest (data _synthetic)
+ "Handle SelectionRequest events on IMS window.
+
+Such events would be received when clients query for LOCALES or TRANSPORT."
+ (exwm--log)
+ (let ((evt (make-instance 'xcb:SelectionRequest))
+ value fake-event)
+ (xcb:unmarshal evt data)
+ (with-slots (time requestor selection target property) evt
+ (setq value (cond ((= target exwm-xim--LOCALES)
+ ;; Return supported locales.
+ exwm-xim--locales)
+ ((= target exwm-xim--TRANSPORT)
+ ;; Use XIM over an X connection.
+ "@transport=X/")))
+ (when value
+ ;; Change the property.
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:ChangeProperty
+ :mode xcb:PropMode:Replace
+ :window requestor
+ :property property
+ :type target
+ :format 8
+ :data-len (length value)
+ :data value))
+ ;; Send a SelectionNotify event.
+ (setq fake-event (make-instance 'xcb:SelectionNotify
+ :time time
+ :requestor requestor
+ :selection selection
+ :target target
+ :property property))
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:SendEvent
+ :propagate 0
+ :destination requestor
+ :event-mask xcb:EventMask:NoEvent
+ :event (xcb:marshal fake-event exwm-xim--conn)))
+ (xcb:flush exwm-xim--conn)))))
+
+(cl-defun exwm-xim--on-ClientMessage-0 (data _synthetic)
+ "Handle ClientMessage event on IMS window (new connection).
+
+Such events would be received when clients request for _XIM_XCONNECT.
+A new X connection and server window would be created to communicate with
+this client."
+ (exwm--log)
+ (let ((evt (make-instance 'xcb:ClientMessage))
+ conn client-xwin server-xwin)
+ (xcb:unmarshal evt data)
+ (with-slots (window type data) evt
+ (unless (= type exwm-xim--_XIM_XCONNECT)
+ ;; Only handle _XIM_XCONNECT.
+ (exwm--log "Ignore ClientMessage %s" type)
+ (cl-return-from exwm-xim--on-ClientMessage-0))
+ (setq client-xwin (elt (slot-value data 'data32) 0)
+ ;; Create a new X connection and a new server window.
+ conn (xcb:connect)
+ server-xwin (xcb:generate-id conn))
+ (set-process-query-on-exit-flag (slot-value conn 'process) nil)
+ ;; Store this client.
+ (plist-put exwm-xim--server-client-plist server-xwin
+ `[,conn ,client-xwin nil])
+ (plist-put exwm-xim--client-server-plist client-xwin server-xwin)
+ ;; Select DestroyNotify events on this client window.
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:ChangeWindowAttributes
+ :window client-xwin
+ :value-mask xcb:CW:EventMask
+ :event-mask xcb:EventMask:StructureNotify))
+ (xcb:flush exwm-xim--conn)
+ ;; Handle ClientMessage events from this new connection.
+ (xcb:+event conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage)
+ ;; Create a communication window.
+ (xcb:+request conn
+ (make-instance 'xcb:CreateWindow
+ :depth 0
+ :wid server-xwin
+ :parent exwm--root
+ :x 0
+ :y 0
+ :width 1
+ :height 1
+ :border-width 0
+ :class xcb:WindowClass:InputOutput
+ :visual 0
+ :value-mask xcb:CW:OverrideRedirect
+ :override-redirect 1))
+ (xcb:flush conn)
+ ;; Send connection establishment ClientMessage.
+ (setf window client-xwin
+ (slot-value data 'data32) `(,server-xwin 0 0 0 0))
+ (slot-makeunbound data 'data8)
+ (slot-makeunbound data 'data16)
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:SendEvent
+ :propagate 0
+ :destination client-xwin
+ :event-mask xcb:EventMask:NoEvent
+ :event (xcb:marshal evt exwm-xim--conn)))
+ (xcb:flush exwm-xim--conn))))
+
+(cl-defun exwm-xim--on-ClientMessage (data _synthetic)
+ "Handle ClientMessage event on IMS communication window (request).
+
+Such events would be received when clients request for _XIM_PROTOCOL.
+The actual XIM request is in client message data or a property."
+ (exwm--log)
+ (let ((evt (make-instance 'xcb:ClientMessage))
+ conn client-xwin server-xwin)
+ (xcb:unmarshal evt data)
+ (with-slots (format window type data) evt
+ (unless (= type exwm-xim--_XIM_PROTOCOL)
+ (exwm--log "Ignore ClientMessage %s" type)
+ (cl-return-from exwm-xim--on-ClientMessage))
+ (setq server-xwin window
+ conn (plist-get exwm-xim--server-client-plist server-xwin)
+ client-xwin (elt conn 1)
+ conn (elt conn 0))
+ (cond ((= format 8)
+ ;; Data.
+ (exwm-xim--on-request (vconcat (slot-value data 'data8))
+ conn client-xwin server-xwin))
+ ((= format 32)
+ ;; Atom.
+ (with-slots (data32) data
+ (with-slots (value)
+ (xcb:+request-unchecked+reply conn
+ (make-instance 'xcb:GetProperty
+ :delete 1
+ :window server-xwin
+ :property (elt data32 1)
+ :type xcb:GetPropertyType:Any
+ :long-offset 0
+ :long-length (elt data32 0)))
+ (when (> (length value) 0)
+ (exwm-xim--on-request value conn client-xwin
+ server-xwin)))))))))
+
+(defun exwm-xim--on-request (data conn client-xwin server-xwin)
+ "Handle an XIM reuqest."
+ (exwm--log)
+ (let ((opcode (elt data 0))
+ ;; Let-bind `xim:lsb' to make pack/unpack functions work correctly.
+ (xim:lsb (elt (plist-get exwm-xim--server-client-plist server-xwin) 2))
+ req replies)
+ (cond ((= opcode xim:opcode:error)
+ (exwm--log "ERROR: %s" data))
+ ((= opcode xim:opcode:connect)
+ (exwm--log "CONNECT")
+ (setq xim:lsb (= (elt data 4) xim:connect-byte-order:lsb-first))
+ ;; Store byte-order.
+ (setf (elt (plist-get exwm-xim--server-client-plist server-xwin) 2)
+ xim:lsb)
+ (setq req (make-instance 'xim:connect))
+ (xcb:unmarshal req data)
+ (if (and (= (slot-value req 'major-version) 1)
+ (= (slot-value req 'minor-version) 0)
+ ;; Do not support authentication.
+ (= (slot-value req 'number) 0))
+ ;; Accept the connection.
+ (push (make-instance 'xim:connect-reply) replies)
+ ;; Deny it.
+ (push exwm-xim--default-error replies)))
+ ((memq opcode (list xim:opcode:auth-required
+ xim:opcode:auth-reply
+ xim:opcode:auth-next
+ xim:opcode:auth-ng))
+ (exwm--log "AUTH: %d" opcode)
+ ;; Deny any attempt to make authentication.
+ (push exwm-xim--default-error replies))
+ ((= opcode xim:opcode:disconnect)
+ (exwm--log "DISCONNECT")
+ ;; Gracefully disconnect from the client.
+ (exwm-xim--make-request (make-instance 'xim:disconnect-reply)
+ conn client-xwin)
+ ;; Destroy the communication window & connection.
+ (xcb:+request conn
+ (make-instance 'xcb:DestroyWindow
+ :window server-xwin))
+ (xcb:disconnect conn)
+ ;; Clean up cache.
+ (cl-remf exwm-xim--server-client-plist server-xwin)
+ (cl-remf exwm-xim--client-server-plist client-xwin))
+ ((= opcode xim:opcode:open)
+ (exwm--log "OPEN")
+ ;; Note: We make no check here.
+ (setq exwm-xim--im-id (if (< exwm-xim--im-id #xffff)
+ (1+ exwm-xim--im-id)
+ 1))
+ (setq replies
+ (list
+ (make-instance 'xim:open-reply
+ :im-id exwm-xim--im-id
+ :im-attrs-length nil
+ :im-attrs exwm-xim--default-im-attrs
+ :ic-attrs-length nil
+ :ic-attrs exwm-xim--default-ic-attrs)
+ (make-instance 'xim:set-event-mask
+ :im-id exwm-xim--im-id
+ :ic-id 0
+ ;; Static event flow.
+ :forward-event-mask xcb:EventMask:KeyPress
+ ;; on-demand-synchronous method.
+ :synchronous-event-mask
+ xcb:EventMask:NoEvent))))
+ ((= opcode xim:opcode:close)
+ (exwm--log "CLOSE")
+ (setq req (make-instance 'xim:close))
+ (xcb:unmarshal req data)
+ (push (make-instance 'xim:close-reply
+ :im-id (slot-value req 'im-id))
+ replies))
+ ((= opcode xim:opcode:trigger-notify)
+ (exwm--log "TRIGGER-NOTIFY")
+ ;; Only static event flow modal is supported.
+ (push exwm-xim--default-error replies))
+ ((= opcode xim:opcode:encoding-negotiation)
+ (exwm--log "ENCODING-NEGOTIATION")
+ (setq req (make-instance 'xim:encoding-negotiation))
+ (xcb:unmarshal req data)
+ (let ((index (cl-position "COMPOUND_TEXT"
+ (mapcar (lambda (i) (slot-value i 'name))
+ (slot-value req 'names))
+ :test #'equal)))
+ (unless index
+ ;; Fallback to portable character encoding (a subset of ASCII).
+ (setq index -1))
+ (push (make-instance 'xim:encoding-negotiation-reply
+ :im-id (slot-value req 'im-id)
+ :category
+ xim:encoding-negotiation-reply-category:name
+ :index index)
+ replies)))
+ ((= opcode xim:opcode:query-extension)
+ (exwm--log "QUERY-EXTENSION")
+ (setq req (make-instance 'xim:query-extension))
+ (xcb:unmarshal req data)
+ (push (make-instance 'xim:query-extension-reply
+ :im-id (slot-value req 'im-id)
+ ;; No extension support.
+ :length 0
+ :extensions nil)
+ replies))
+ ((= opcode xim:opcode:set-im-values)
+ (exwm--log "SET-IM-VALUES")
+ ;; There's only one possible input method attribute.
+ (setq req (make-instance 'xim:set-im-values))
+ (xcb:unmarshal req data)
+ (push (make-instance 'xim:set-im-values-reply
+ :im-id (slot-value req 'im-id))
+ replies))
+ ((= opcode xim:opcode:get-im-values)
+ (exwm--log "GET-IM-VALUES")
+ (setq req (make-instance 'xim:get-im-values))
+ (let (im-attributes-id)
+ (xcb:unmarshal req data)
+ (setq im-attributes-id (slot-value req 'im-attributes-id))
+ (if (cl-notevery (lambda (i) (= i 0)) im-attributes-id)
+ ;; Only support one IM attributes.
+ (push (make-instance 'xim:error
+ :im-id (slot-value req 'im-id)
+ :ic-id 0
+ :flag xim:error-flag:invalid-ic-id
+ :error-code xim:error-code:bad-something
+ :length 0
+ :type 0
+ :detail nil)
+ replies)
+ (push
+ (make-instance 'xim:get-im-values-reply
+ :im-id (slot-value req 'im-id)
+ :length nil
+ :im-attributes exwm-xim--default-attributes)
+ replies))))
+ ((= opcode xim:opcode:create-ic)
+ (exwm--log "CREATE-IC")
+ (setq req (make-instance 'xim:create-ic))
+ (xcb:unmarshal req data)
+ ;; Note: The ic-attributes slot is ignored.
+ (setq exwm-xim--ic-id (if (< exwm-xim--ic-id #xffff)
+ (1+ exwm-xim--ic-id)
+ 1))
+ (push (make-instance 'xim:create-ic-reply
+ :im-id (slot-value req 'im-id)
+ :ic-id exwm-xim--ic-id)
+ replies))
+ ((= opcode xim:opcode:destroy-ic)
+ (exwm--log "DESTROY-IC")
+ (setq req (make-instance 'xim:destroy-ic))
+ (xcb:unmarshal req data)
+ (push (make-instance 'xim:destroy-ic-reply
+ :im-id (slot-value req 'im-id)
+ :ic-id (slot-value req 'ic-id))
+ replies))
+ ((= opcode xim:opcode:set-ic-values)
+ (exwm--log "SET-IC-VALUES")
+ (setq req (make-instance 'xim:set-ic-values))
+ (xcb:unmarshal req data)
+ ;; We don't distinguish between input contexts.
+ (push (make-instance 'xim:set-ic-values-reply
+ :im-id (slot-value req 'im-id)
+ :ic-id (slot-value req 'ic-id))
+ replies))
+ ((= opcode xim:opcode:get-ic-values)
+ (exwm--log "GET-IC-VALUES")
+ (setq req (make-instance 'xim:get-ic-values))
+ (xcb:unmarshal req data)
+ (push (make-instance 'xim:get-ic-values-reply
+ :im-id (slot-value req 'im-id)
+ :ic-id (slot-value req 'ic-id)
+ :length nil
+ :ic-attributes exwm-xim--default-attributes)
+ replies))
+ ((= opcode xim:opcode:set-ic-focus)
+ (exwm--log "SET-IC-FOCUS")
+ ;; All input contexts are the same.
+ )
+ ((= opcode xim:opcode:unset-ic-focus)
+ (exwm--log "UNSET-IC-FOCUS")
+ ;; All input contexts are the same.
+ )
+ ((= opcode xim:opcode:forward-event)
+ (exwm--log "FORWARD-EVENT")
+ (setq req (make-instance 'xim:forward-event))
+ (xcb:unmarshal req data)
+ (let ((im-func (with-current-buffer (window-buffer)
+ input-method-function))
+ key-event keysym event result)
+ ;; Note: The flag slot is ignored.
+ ;; Do conversion in client's byte-order.
+ (let ((xcb:lsb xim:lsb))
+ (setq key-event (make-instance 'xcb:KeyPress))
+ (xcb:unmarshal key-event (slot-value req 'event)))
+ (with-slots (detail state) key-event
+ (setq keysym (xcb:keysyms:keycode->keysym exwm-xim--conn detail
+ state))
+ (when (/= (car keysym) 0)
+ (setq event (xcb:keysyms:keysym->event
+ exwm-xim--conn
+ (car keysym)
+ (logand state (lognot (cdr keysym)))))))
+ (if exwm-xim--event-pending
+ ;; In case any event reaches here, it should be forwarded
+ ;; to Emacs.
+ (when event
+ (setq unread-command-events
+ (append unread-command-events (list event))))
+ (setq exwm-xim--event-pending t)
+ (if (or (not im-func)
+ ;; `list' is the default method.
+ (eq im-func #'list)
+ (not event)
+ ;; Select only printable keys.
+ (not (integerp event)) (> #x20 event) (< #x7e event))
+ ;; Either there is no active input method, or invalid key
+ ;; is detected.
+ (with-slots (im-id ic-id serial-number event) req
+ (push (make-instance 'xim:forward-event
+ :im-id im-id
+ :ic-id ic-id
+ :flag xim:commit-flag:synchronous
+ :serial-number serial-number
+ :event event)
+ replies))
+ (when (eq exwm--selected-input-mode 'char-mode)
+ ;; Grab keyboard temporarily for char-mode.
+ (exwm-input--grab-keyboard))
+ (unwind-protect
+ (with-temp-buffer
+ ;; Always show key strokes.
+ (let ((input-method-use-echo-area t))
+ (setq result (funcall im-func event))))
+ (when (eq exwm--selected-input-mode 'char-mode)
+ (exwm-input--release-keyboard)))
+ ;; This also works for portable character encoding.
+ (setq result
+ (encode-coding-string (concat result)
+ 'compound-text-with-extensions))
+ (message "")
+ (push
+ (make-instance 'xim:commit-x-lookup-chars
+ :im-id (slot-value req 'im-id)
+ :ic-id (slot-value req 'ic-id)
+ :flag (logior xim:commit-flag:synchronous
+ xim:commit-flag:x-lookup-chars)
+ :length (length result)
+ :string result)
+ replies))
+ (setq exwm-xim--event-pending nil))))
+ ((= opcode xim:opcode:sync)
+ (exwm--log "SYNC")
+ (setq req (make-instance 'xim:sync))
+ (xcb:unmarshal req data)
+ (push (make-instance 'xim:sync-reply
+ :im-id (slot-value req 'im-id)
+ :ic-id (slot-value req 'ic-id))
+ replies))
+ ((= opcode xim:opcode:sync-reply)
+ (exwm--log "SYNC-REPLY"))
+ ((= opcode xim:opcode:reset-ic)
+ (exwm--log "RESET-IC")
+ ;; No context-specific data saved.
+ (setq req (make-instance 'xim:reset-ic))
+ (xcb:unmarshal req data)
+ (push (make-instance 'xim:reset-ic-reply
+ :im-id (slot-value req 'im-id)
+ :ic-id (slot-value req 'ic-id)
+ :length 0
+ :string "")
+ replies))
+ ((memq opcode (list xim:opcode:str-conversion-reply
+ xim:opcode:preedit-start-reply
+ xim:opcode:preedit-caret-reply))
+ (exwm--log "PREEDIT: %d" opcode)
+ ;; No preedit support.
+ (push exwm-xim--default-error replies))
+ (t
+ (exwm--log "Bad protocol")
+ (push exwm-xim--default-error replies)))
+ ;; Actually send the replies.
+ (when replies
+ (mapc (lambda (reply)
+ (exwm-xim--make-request reply conn client-xwin))
+ replies)
+ (xcb:flush conn))))
+
+(defun exwm-xim--make-request (req conn client-xwin)
+ "Make an XIM request REQ via connection CONN.
+
+CLIENT-XWIN would receive a ClientMessage event either telling the client
+the request data or where to fetch the data."
+ (exwm--log)
+ (let ((data (xcb:marshal req))
+ property format client-message-data client-message)
+ (if (<= (length data) 20)
+ ;; Send short requests directly with client messages.
+ (setq format 8
+ ;; Pad to 20 bytes.
+ data (append data (make-list (- 20 (length data)) 0))
+ client-message-data (make-instance 'xcb:ClientMessageData
+ :data8 data))
+ ;; Send long requests with properties.
+ (setq property (exwm--intern-atom (format "_EXWM_XIM_%x"
+ exwm-xim--property-index)))
+ (cl-incf exwm-xim--property-index)
+ (xcb:+request conn
+ (make-instance 'xcb:ChangeProperty
+ :mode xcb:PropMode:Append
+ :window client-xwin
+ :property property
+ :type xcb:Atom:STRING
+ :format 8
+ :data-len (length data)
+ :data data))
+ ;; Also send a client message to notify the client about this property.
+ (setq format 32
+ client-message-data (make-instance 'xcb:ClientMessageData
+ :data32 `(,(length data)
+ ,property
+ ;; Pad to 20 bytes.
+ 0 0 0))))
+ ;; Send the client message.
+ (setq client-message (make-instance 'xcb:ClientMessage
+ :format format
+ :window client-xwin
+ :type exwm-xim--_XIM_PROTOCOL
+ :data client-message-data))
+ (xcb:+request conn
+ (make-instance 'xcb:SendEvent
+ :propagate 0
+ :destination client-xwin
+ :event-mask xcb:EventMask:NoEvent
+ :event (xcb:marshal client-message conn)))))
+
+(defun exwm-xim--on-DestroyNotify (data synthetic)
+ "Do cleanups on receiving DestroyNotify event.
+
+Such event would be received when the client window is destroyed."
+ (exwm--log)
+ (unless synthetic
+ (let ((evt (make-instance 'xcb:DestroyNotify))
+ conn client-xwin server-xwin)
+ (xcb:unmarshal evt data)
+ (setq client-xwin (slot-value evt 'window)
+ server-xwin (plist-get exwm-xim--client-server-plist client-xwin))
+ (when server-xwin
+ (setq conn (aref (plist-get exwm-xim--server-client-plist server-xwin)
+ 0))
+ (cl-remf exwm-xim--server-client-plist server-xwin)
+ (cl-remf exwm-xim--client-server-plist client-xwin)
+ ;; Destroy the communication window & connection.
+ (xcb:+request conn
+ (make-instance 'xcb:DestroyWindow
+ :window server-xwin))
+ (xcb:disconnect conn)))))
+
+(cl-defun exwm-xim--init ()
+ "Initialize the XIM module."
+ (exwm--log)
+ (when exwm-xim--conn
+ (cl-return-from exwm-xim--init))
+ ;; Initialize atoms.
+ (setq address@hidden (exwm--intern-atom "@server=exwm-xim")
+ exwm-xim--LOCALES (exwm--intern-atom "LOCALES")
+ exwm-xim--TRANSPORT (exwm--intern-atom "TRANSPORT")
+ exwm-xim--XIM_SERVERS (exwm--intern-atom "XIM_SERVERS")
+ exwm-xim--_XIM_PROTOCOL (exwm--intern-atom "_XIM_PROTOCOL")
+ exwm-xim--_XIM_XCONNECT (exwm--intern-atom "_XIM_XCONNECT"))
+ ;; Create a new connection and event window.
+ (setq exwm-xim--conn (xcb:connect)
+ exwm-xim--event-xwin (xcb:generate-id exwm-xim--conn))
+ (set-process-query-on-exit-flag (slot-value exwm-xim--conn 'process) nil)
+ ;; Initialize xcb:keysyms module.
+ (xcb:keysyms:init exwm-xim--conn)
+ ;; Listen to SelectionRequest event for connection establishment.
+ (xcb:+event exwm-xim--conn 'xcb:SelectionRequest
+ #'exwm-xim--on-SelectionRequest)
+ ;; Listen to ClientMessage event on IMS window for new XIM connection.
+ (xcb:+event exwm-xim--conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage-0)
+ ;; Listen to DestroyNotify event to do cleanups.
+ (xcb:+event exwm-xim--conn 'xcb:DestroyNotify #'exwm-xim--on-DestroyNotify)
+ ;; Create the event window.
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:CreateWindow
+ :depth 0
+ :wid exwm-xim--event-xwin
+ :parent exwm--root
+ :x 0
+ :y 0
+ :width 1
+ :height 1
+ :border-width 0
+ :class xcb:WindowClass:InputOutput
+ :visual 0
+ :value-mask xcb:CW:OverrideRedirect
+ :override-redirect 1))
+ ;; Set the selection owner.
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:SetSelectionOwner
+ :owner exwm-xim--event-xwin
+ :selection address@hidden
+ :time xcb:Time:CurrentTime))
+ ;; Set XIM_SERVERS property on the root window.
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:ChangeProperty
+ :mode xcb:PropMode:Prepend
+ :window exwm--root
+ :property exwm-xim--XIM_SERVERS
+ :type xcb:Atom:ATOM
+ :format 32
+ :data-len 1
+ :data (funcall (if xcb:lsb
+ #'xcb:-pack-u4-lsb
+ #'xcb:-pack-u4)
+ address@hidden)))
+ (xcb:flush exwm-xim--conn))
+
+(cl-defun exwm-xim--exit ()
+ "Exit the XIM module."
+ (exwm--log)
+ ;; Close IMS communication connections.
+ (mapc (lambda (i)
+ (when (vectorp i)
+ (xcb:disconnect (elt i 0))))
+ exwm-xim--server-client-plist)
+ ;; Close the IMS connection.
+ (unless exwm-xim--conn
+ (cl-return-from exwm-xim--exit))
+ ;; Remove exwm-xim from XIM_SERVERS.
+ (let ((reply (xcb:+request-unchecked+reply exwm-xim--conn
+ (make-instance 'xcb:GetProperty
+ :delete 1
+ :window exwm--root
+ :property exwm-xim--XIM_SERVERS
+ :type xcb:Atom:ATOM
+ :long-offset 0
+ :long-length 1000)))
+ unpacked-reply pack unpack)
+ (unless reply
+ (cl-return-from exwm-xim--exit))
+ (setq reply (slot-value reply 'value))
+ (unless (> (length reply) 4)
+ (cl-return-from exwm-xim--exit))
+ (setq reply (vconcat reply)
+ pack (if xcb:lsb #'xcb:-pack-u4-lsb #'xcb:-pack-u4)
+ unpack (if xcb:lsb #'xcb:-unpack-u4-lsb #'xcb:-unpack-u4))
+ (dotimes (i (/ (length reply) 4))
+ (push (funcall unpack reply (* i 4)) unpacked-reply))
+ (setq unpacked-reply (delq address@hidden unpacked-reply)
+ reply (mapcar pack unpacked-reply))
+ (xcb:+request exwm-xim--conn
+ (make-instance 'xcb:ChangeProperty
+ :mode xcb:PropMode:Replace
+ :window exwm--root
+ :property exwm-xim--XIM_SERVERS
+ :type xcb:Atom:ATOM
+ :format 32
+ :data-len (length reply)
+ :data reply))
+ (xcb:flush exwm-xim--conn))
+ (xcb:disconnect exwm-xim--conn)
+ (setq exwm-xim--conn nil))
+
+(defun exwm-xim-enable ()
+ "Enable XIM support for EXWM."
+ (exwm--log)
+ (add-hook 'exwm-init-hook #'exwm-xim--init)
+ (add-hook 'exwm-exit-hook #'exwm-xim--exit))
+
+
+
+(provide 'exwm-xim)
+
+;;; exwm-xim.el ends here
diff --git a/xinitrc b/xinitrc
index 0adc068..591e419 100644
--- a/xinitrc
+++ b/xinitrc
@@ -1,17 +1,20 @@
-# Disable access control
+# Disable access control for the current user.
xhost +SI:localuser:$USER
# Make Java applications aware this is a non-reparenting window manager.
export _JAVA_AWT_WM_NONREPARENTING=1
-# Themes, etc
-gnome-settings-daemon &
-
-# Fallback cursor
+# Set default cursor.
xsetroot -cursor_name left_ptr
-# Keyboard repeat rate
+# Set keyboard repeat rate.
xset r rate 200 60
-# Start Emacs
-exec dbus-launch --exit-with-session emacs
+# Uncomment the following block to use the exwm-xim module.
+#export address@hidden
+#export GTK_IM_MODULE=xim
+#export QT_IM_MODULE=xim
+#export CLUTTER_IM_MODULE=xim
+
+# Finally start Emacs
+exec emacs
- [elpa] externals/exwm updated (4462981 -> 225c68d), Chris Feng, 2019/02/06
- [elpa] externals/exwm 97b1fb7 4/7: Do the same for `exwm-input-global-keys', Chris Feng, 2019/02/06
- [elpa] externals/exwm ec108a6 3/7: Cleanup simulation key config issues, Chris Feng, 2019/02/06
- [elpa] externals/exwm cd7b32d 2/7: Hide minibuffer upon receiving any event, Chris Feng, 2019/02/06
- [elpa] externals/exwm 225c68d 7/7: Merge branch 'retain-echo-area-until-input' of https://github.com/medranocalvo/exwm into medranocalvo/retain-echo-area-until-input, Chris Feng, 2019/02/06
- [elpa] externals/exwm e157282 6/7: Merge branch 'ieure/default-simulation-keys' into externals/exwm, Chris Feng, 2019/02/06
- [elpa] externals/exwm 0dd909a 1/7: Stop hiding the minibuffer when a message is being displayed, Chris Feng, 2019/02/06
- [elpa] externals/exwm 0f7269c 5/7: Add input method support,
Chris Feng <=