2015-07-17 13:16:08 +02:00
|
|
|
|
;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*-
|
|
|
|
|
|
2017-02-05 10:49:42 +01:00
|
|
|
|
;; Copyright (C) 2015-2017 Free Software Foundation, Inc.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; This file is part of GNU Emacs.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; 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.
|
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; 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
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; This module deals with key/mouse matters, including:
|
|
|
|
|
;; + Input focus,
|
|
|
|
|
;; + Key/Button event handling,
|
|
|
|
|
;; + Key events filtering and simulation.
|
|
|
|
|
|
|
|
|
|
;; Todo:
|
|
|
|
|
;; + Pointer simulation mode (e.g. 'C-c 1'/'C-c 2' for single/double click,
|
|
|
|
|
;; move with arrow keys).
|
|
|
|
|
;; + Simulation keys to mimic Emacs key bindings for text edit (redo, select,
|
2017-02-05 10:50:52 +01:00
|
|
|
|
;; cancel, clear, etc). Some of them are not present on common keyboard
|
|
|
|
|
;; (keycode = 0). May need to use XKB extension.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(require 'xcb-keysyms)
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(require 'exwm-core)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
(defvar exwm-input-move-event 's-down-mouse-1
|
|
|
|
|
"Emacs event to start moving a window.")
|
|
|
|
|
(defvar exwm-input-resize-event 's-down-mouse-3
|
|
|
|
|
"Emacs event to start resizing a window.")
|
|
|
|
|
|
|
|
|
|
(defvar exwm-input--move-keysym nil)
|
|
|
|
|
(defvar exwm-input--move-mask nil)
|
|
|
|
|
(defvar exwm-input--resize-keysym nil)
|
|
|
|
|
(defvar exwm-input--resize-mask nil)
|
|
|
|
|
|
2016-08-09 07:34:29 +02:00
|
|
|
|
(defvar exwm-input--timestamp-window nil)
|
|
|
|
|
(defvar exwm-input--timestamp-atom nil)
|
|
|
|
|
(defvar exwm-input--timestamp-callback nil)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-08-06 15:38:43 +02:00
|
|
|
|
(defvar exwm-workspace--current)
|
|
|
|
|
(defvar exwm-workspace--switch-history-outdated)
|
|
|
|
|
(defvar exwm-workspace-current-index)
|
|
|
|
|
(defvar exwm-workspace--minibuffer)
|
|
|
|
|
(defvar exwm-workspace--list)
|
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input--set-focus (id)
|
|
|
|
|
"Set input focus to window ID in a proper way."
|
2015-08-16 20:53:04 +02:00
|
|
|
|
(when (exwm--id->buffer id)
|
2016-08-14 06:24:40 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
|
|
|
|
(cond
|
|
|
|
|
((and (not exwm--hints-input)
|
|
|
|
|
(memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))
|
|
|
|
|
(when (= (frame-parameter nil 'exwm-id)
|
|
|
|
|
(slot-value (xcb:+request-unchecked+reply exwm--connection
|
2016-08-09 07:34:29 +02:00
|
|
|
|
(make-instance 'xcb:GetInputFocus))
|
2016-08-14 06:24:40 +02:00
|
|
|
|
'focus))
|
|
|
|
|
(exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id)
|
|
|
|
|
(exwm-input--update-timestamp
|
|
|
|
|
(lambda (timestamp id)
|
|
|
|
|
(let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS
|
|
|
|
|
:window id
|
|
|
|
|
:time timestamp)))
|
|
|
|
|
(setq event (xcb:marshal event exwm--connection))
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:icccm:SendEvent
|
|
|
|
|
:destination id
|
|
|
|
|
:event event))
|
|
|
|
|
(exwm-input--set-active-window id)
|
|
|
|
|
(xcb:flush exwm--connection)))
|
|
|
|
|
id)))
|
|
|
|
|
(t
|
|
|
|
|
(exwm--log "Focus on #x%x with SetInputFocus" id)
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:SetInputFocus
|
|
|
|
|
:revert-to xcb:InputFocus:Parent
|
|
|
|
|
:focus id
|
|
|
|
|
:time xcb:Time:CurrentTime))
|
|
|
|
|
(exwm-input--set-active-window id)
|
|
|
|
|
(xcb:flush exwm--connection))))))
|
2016-08-09 07:34:29 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm-input--update-timestamp (callback &rest args)
|
|
|
|
|
"Fetch the latest timestamp from the server and feed it to CALLBACK.
|
|
|
|
|
|
|
|
|
|
ARGS are additional arguments to CALLBACK."
|
|
|
|
|
(setq exwm-input--timestamp-callback (cons callback args))
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:ChangeProperty
|
|
|
|
|
:mode xcb:PropMode:Replace
|
|
|
|
|
:window exwm-input--timestamp-window
|
|
|
|
|
:property exwm-input--timestamp-atom
|
|
|
|
|
:type xcb:Atom:CARDINAL
|
|
|
|
|
:format 32
|
|
|
|
|
:data-len 0
|
|
|
|
|
:data nil))
|
|
|
|
|
(xcb:flush exwm--connection))
|
|
|
|
|
|
|
|
|
|
(defun exwm-input--on-PropertyNotify (data _synthetic)
|
|
|
|
|
"Handle PropertyNotify events."
|
|
|
|
|
(when exwm-input--timestamp-callback
|
|
|
|
|
(let ((obj (make-instance 'xcb:PropertyNotify)))
|
|
|
|
|
(xcb:unmarshal obj data)
|
|
|
|
|
(when (= exwm-input--timestamp-window
|
|
|
|
|
(slot-value obj 'window))
|
|
|
|
|
(apply (car exwm-input--timestamp-callback)
|
|
|
|
|
(slot-value obj 'time)
|
|
|
|
|
(cdr exwm-input--timestamp-callback))
|
|
|
|
|
(setq exwm-input--timestamp-callback nil)))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-09-22 13:02:54 +02:00
|
|
|
|
(defun exwm-input--on-FocusIn (&rest _args)
|
2016-08-06 15:38:43 +02:00
|
|
|
|
"Handle FocusIn events."
|
2016-09-22 13:02:54 +02:00
|
|
|
|
;; Not sure if this is the right thing to do but the point is the
|
|
|
|
|
;; input focus should not stay at the root window or any container,
|
|
|
|
|
;; or the result would be unpredictable. `x-focus-frame' would
|
|
|
|
|
;; first set the input focus to the (previously) selected frame, and
|
|
|
|
|
;; then `select-window' would further update the input focus if the
|
|
|
|
|
;; selected window is displaying an `exwm-mode' buffer. Perhaps we
|
|
|
|
|
;; should carefully filter out FocusIn events with certain 'detail'
|
|
|
|
|
;; and 'mode' combinations, but this just works.
|
|
|
|
|
(x-focus-frame (selected-frame))
|
|
|
|
|
(select-window (selected-window)))
|
2016-08-06 15:38:43 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm-input--on-workspace-list-change ()
|
|
|
|
|
"Run in `exwm-input--update-global-prefix-keys'."
|
|
|
|
|
(dolist (f exwm-workspace--list)
|
|
|
|
|
;; Reuse the 'exwm-grabbed' frame parameter set in
|
|
|
|
|
;; `exwm-input--update-global-prefix-keys'.
|
|
|
|
|
(unless (frame-parameter f 'exwm-grabbed)
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:ChangeWindowAttributes
|
|
|
|
|
:window (frame-parameter f 'exwm-workspace)
|
|
|
|
|
:value-mask xcb:CW:EventMask
|
|
|
|
|
;; There should no other event selected there.
|
|
|
|
|
:event-mask xcb:EventMask:FocusChange))))
|
|
|
|
|
(exwm-input--update-global-prefix-keys)
|
|
|
|
|
(xcb:flush exwm--connection))
|
|
|
|
|
|
2016-08-01 13:53:04 +02:00
|
|
|
|
(declare-function exwm-workspace--client-p "exwm-workspace.el"
|
|
|
|
|
(&optional frame))
|
|
|
|
|
|
2016-07-21 06:51:37 +02:00
|
|
|
|
(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused.
|
|
|
|
|
|
|
|
|
|
This value should always be overwritten.")
|
2015-08-12 12:09:35 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm-input--on-buffer-list-update ()
|
2016-07-21 06:51:37 +02:00
|
|
|
|
"Run in `buffer-list-update-hook' to track input focus."
|
2016-10-06 06:47:56 +02:00
|
|
|
|
(when (and (eq (current-buffer) (window-buffer)) ;e.g. `with-temp-buffer'.
|
2016-09-23 12:24:37 +02:00
|
|
|
|
(not (eq this-command #'handle-switch-frame))
|
2016-08-01 13:53:04 +02:00
|
|
|
|
(not (exwm-workspace--client-p)))
|
2016-07-21 06:51:37 +02:00
|
|
|
|
(setq exwm-input--update-focus-window (selected-window))
|
|
|
|
|
(exwm-input--update-focus-defer)))
|
|
|
|
|
|
|
|
|
|
;; Input focus update requests should be accumulated for a short time
|
|
|
|
|
;; interval so that only the last one need to be processed. This not
|
|
|
|
|
;; improves the overall performance, but avoids the problem of input
|
|
|
|
|
;; focus loop, which is a result of the interaction with Emacs frames.
|
|
|
|
|
;;
|
|
|
|
|
;; FIXME: The time interval is hard to decide and perhaps machine-dependent.
|
|
|
|
|
;; A value too small can cause redundant updates of input focus,
|
|
|
|
|
;; and even worse, dead loops. OTOH a large value would bring
|
|
|
|
|
;; laggy experience.
|
|
|
|
|
(defconst exwm-input--update-focus-interval 0.01
|
|
|
|
|
"Time interval (in seconds) for accumulating input focus update requests.")
|
|
|
|
|
|
|
|
|
|
(defvar exwm-input--update-focus-lock nil
|
|
|
|
|
"Lock for solving input focus update contention.")
|
|
|
|
|
(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.")
|
|
|
|
|
(defvar exwm-input--update-focus-timer nil
|
|
|
|
|
"Timer for deferring the update of input focus.")
|
|
|
|
|
|
|
|
|
|
(defun exwm-input--update-focus-defer ()
|
|
|
|
|
"Defer updating input focus."
|
|
|
|
|
(when exwm-input--update-focus-defer-timer
|
|
|
|
|
(cancel-timer exwm-input--update-focus-defer-timer))
|
|
|
|
|
(if exwm-input--update-focus-lock
|
|
|
|
|
(setq exwm-input--update-focus-defer-timer
|
|
|
|
|
(run-with-idle-timer 0 nil
|
|
|
|
|
#'exwm-input--update-focus-defer))
|
|
|
|
|
(setq exwm-input--update-focus-defer-timer nil)
|
|
|
|
|
(when exwm-input--update-focus-timer
|
|
|
|
|
(cancel-timer exwm-input--update-focus-timer))
|
|
|
|
|
(setq exwm-input--update-focus-timer
|
|
|
|
|
(run-with-idle-timer exwm-input--update-focus-interval nil
|
|
|
|
|
#'exwm-input--update-focus
|
|
|
|
|
exwm-input--update-focus-window))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-07-13 12:51:32 +02:00
|
|
|
|
(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
|
|
|
|
|
(declare-function exwm-layout--set-state "exwm-layout.el" (id state))
|
2016-07-17 14:00:00 +02:00
|
|
|
|
(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
|
|
|
|
|
(declare-function exwm-workspace-switch "exwm-workspace.el"
|
2016-07-17 14:00:00 +02:00
|
|
|
|
(frame-or-index &optional force))
|
2016-07-31 07:16:51 +02:00
|
|
|
|
(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace))
|
2016-07-13 12:51:32 +02:00
|
|
|
|
|
2016-07-21 06:51:37 +02:00
|
|
|
|
(defun exwm-input--update-focus (window)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"Update input focus."
|
2016-07-21 06:51:37 +02:00
|
|
|
|
(setq exwm-input--update-focus-lock t)
|
2016-10-06 06:47:56 +02:00
|
|
|
|
(when (window-live-p window)
|
2016-07-21 06:51:37 +02:00
|
|
|
|
(with-current-buffer (window-buffer window)
|
2015-08-12 12:09:35 +02:00
|
|
|
|
(if (eq major-mode 'exwm-mode)
|
2015-09-20 04:51:58 +02:00
|
|
|
|
(if (not (eq exwm--frame exwm-workspace--current))
|
2016-10-06 13:26:53 +02:00
|
|
|
|
(progn
|
|
|
|
|
(set-frame-parameter exwm--frame 'exwm-selected-window window)
|
|
|
|
|
(run-with-idle-timer 0 nil #'exwm-workspace-switch
|
|
|
|
|
exwm--frame))
|
2015-08-13 09:33:02 +02:00
|
|
|
|
(exwm--log "Set focus on #x%x" exwm--id)
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(exwm-input--set-focus exwm--id)
|
2016-02-06 05:59:33 +01:00
|
|
|
|
(when exwm--floating-frame
|
2016-02-20 14:52:07 +01:00
|
|
|
|
;; Adjust stacking orders of the floating container.
|
2016-07-19 13:23:37 +02:00
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:ConfigureWindow
|
|
|
|
|
:window exwm--container
|
|
|
|
|
:value-mask xcb:ConfigWindow:StackMode
|
|
|
|
|
:stack-mode xcb:StackMode:Above))
|
2016-07-13 12:51:32 +02:00
|
|
|
|
;; This floating X window might be hide by `exwm-floating-hide'.
|
|
|
|
|
(when (exwm-layout--iconic-state-p)
|
|
|
|
|
(exwm-layout--set-state exwm--id
|
|
|
|
|
xcb:icccm:WM_STATE:NormalState))
|
2016-02-20 14:52:07 +01:00
|
|
|
|
(xcb:flush exwm--connection)))
|
2016-07-21 06:51:37 +02:00
|
|
|
|
(when (eq (selected-window) window)
|
|
|
|
|
(exwm--log "Focus on %s" window)
|
2016-07-30 04:00:11 +02:00
|
|
|
|
(if (and (exwm-workspace--workspace-p (selected-frame))
|
|
|
|
|
(not (eq (selected-frame) exwm-workspace--current)))
|
|
|
|
|
;; The focus is on another workspace (e.g. it got clicked)
|
|
|
|
|
;; so switch to it.
|
2016-10-06 13:26:53 +02:00
|
|
|
|
(progn
|
|
|
|
|
(set-frame-parameter (selected-frame) 'exwm-selected-window
|
|
|
|
|
window)
|
|
|
|
|
(run-with-idle-timer 0 nil #'exwm-workspace-switch
|
|
|
|
|
(selected-frame)))
|
2016-07-30 04:00:11 +02:00
|
|
|
|
;; The focus is still on the current workspace.
|
2016-10-06 06:47:56 +02:00
|
|
|
|
(if (not (and (exwm-workspace--minibuffer-own-frame-p)
|
|
|
|
|
(minibufferp)))
|
|
|
|
|
(select-frame-set-input-focus (window-frame window) t)
|
|
|
|
|
;; X input focus should be set on the previously selected
|
|
|
|
|
;; frame.
|
|
|
|
|
(select-frame-set-input-focus (window-frame
|
|
|
|
|
(minibuffer-selected-window))
|
|
|
|
|
t)
|
|
|
|
|
(select-frame (window-frame window) t))
|
2016-07-30 04:00:11 +02:00
|
|
|
|
(exwm-input--set-active-window)
|
|
|
|
|
(xcb:flush exwm--connection))))))
|
2016-07-21 06:51:37 +02:00
|
|
|
|
(setq exwm-input--update-focus-lock nil))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-07-26 15:42:22 +02:00
|
|
|
|
(defun exwm-input--on-minibuffer-setup ()
|
|
|
|
|
"Run in `minibuffer-setup-hook' to set input focus."
|
2016-08-01 13:53:04 +02:00
|
|
|
|
(unless (exwm-workspace--client-p)
|
|
|
|
|
;; Set input focus on the Emacs frame
|
|
|
|
|
(x-focus-frame (window-frame (minibuffer-selected-window)))))
|
2016-07-26 15:42:22 +02:00
|
|
|
|
|
2016-07-13 12:51:32 +02:00
|
|
|
|
(defun exwm-input--set-active-window (&optional id)
|
|
|
|
|
"Set _NET_ACTIVE_WINDOW."
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:ewmh:set-_NET_ACTIVE_WINDOW
|
|
|
|
|
:window exwm--root
|
|
|
|
|
:data (or id xcb:Window:None))))
|
|
|
|
|
|
2016-02-19 10:12:43 +01:00
|
|
|
|
(declare-function exwm-floating--start-moveresize "exwm-floating.el"
|
|
|
|
|
(id &optional type))
|
2016-07-17 14:00:00 +02:00
|
|
|
|
(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
|
2016-02-19 10:12:43 +01:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(defun exwm-input--on-ButtonPress (data _synthetic)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"Handle ButtonPress event."
|
|
|
|
|
(let ((obj (make-instance 'xcb:ButtonPress))
|
2016-07-22 06:31:22 +02:00
|
|
|
|
(mode xcb:Allow:SyncPointer)
|
|
|
|
|
window buffer frame)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(xcb:unmarshal obj data)
|
|
|
|
|
(with-slots (detail time event state) obj
|
2016-07-22 06:31:22 +02:00
|
|
|
|
(setq window (get-buffer-window (exwm--id->buffer event) t)
|
|
|
|
|
buffer (window-buffer window))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(cond ((and (= state exwm-input--move-mask)
|
2016-07-22 06:31:22 +02:00
|
|
|
|
(= detail exwm-input--move-keysym)
|
|
|
|
|
;; Either an undecorated or a floating X window.
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(or (not (eq major-mode 'exwm-mode))
|
|
|
|
|
exwm--floating-frame)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Move
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(exwm-floating--start-moveresize
|
|
|
|
|
event xcb:ewmh:_NET_WM_MOVERESIZE_MOVE))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
((and (= state exwm-input--resize-mask)
|
2016-07-22 06:31:22 +02:00
|
|
|
|
(= detail exwm-input--resize-keysym)
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(or (not (eq major-mode 'exwm-mode))
|
|
|
|
|
exwm--floating-frame)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Resize
|
|
|
|
|
(exwm-floating--start-moveresize event))
|
|
|
|
|
(t
|
2015-08-13 01:54:19 +02:00
|
|
|
|
;; Click to focus
|
2016-07-22 06:31:22 +02:00
|
|
|
|
(unless (eq window (selected-window))
|
|
|
|
|
(setq frame (window-frame window))
|
|
|
|
|
(unless (eq frame exwm-workspace--current)
|
|
|
|
|
(if (exwm-workspace--workspace-p frame)
|
|
|
|
|
;; The X window is on another workspace
|
|
|
|
|
(exwm-workspace-switch frame)
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(when (and (eq major-mode 'exwm-mode)
|
|
|
|
|
(not (eq exwm--frame
|
|
|
|
|
exwm-workspace--current)))
|
|
|
|
|
;; The floating X window is on another workspace
|
|
|
|
|
(exwm-workspace-switch exwm--frame)))))
|
|
|
|
|
;; It has been reported that the `window' may have be deleted
|
|
|
|
|
(if (window-live-p window)
|
|
|
|
|
(select-window window)
|
|
|
|
|
(setq window
|
|
|
|
|
(get-buffer-window (exwm--id->buffer event) t))
|
|
|
|
|
(when window (select-window window))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; The event should be replayed
|
|
|
|
|
(setq mode xcb:Allow:ReplayPointer))))
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime))
|
|
|
|
|
(xcb:flush exwm--connection)))
|
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(defun exwm-input--on-KeyPress (data _synthetic)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"Handle KeyPress event."
|
|
|
|
|
(let ((obj (make-instance 'xcb:KeyPress)))
|
|
|
|
|
(xcb:unmarshal obj data)
|
2015-08-07 06:41:15 +02:00
|
|
|
|
(if (eq major-mode 'exwm-mode)
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(funcall exwm--on-KeyPress obj data)
|
2015-08-07 06:41:15 +02:00
|
|
|
|
(exwm-input--on-KeyPress-char-mode obj))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
(defvar exwm-input--global-keys nil "Global key bindings.")
|
|
|
|
|
(defvar exwm-input--global-prefix-keys nil
|
|
|
|
|
"List of prefix keys of global key bindings.")
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(defvar exwm-input-prefix-keys
|
2015-09-23 06:53:08 +02:00
|
|
|
|
'(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:)
|
2015-09-04 03:09:59 +02:00
|
|
|
|
"List of prefix keys EXWM should forward to Emacs when in line-mode.")
|
|
|
|
|
(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.")
|
|
|
|
|
(defvar exwm-input--simulation-prefix-keys nil
|
|
|
|
|
"List of prefix keys of simulation keys in line-mode.")
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm-input--update-global-prefix-keys ()
|
|
|
|
|
"Update `exwm-input--global-prefix-keys'."
|
|
|
|
|
(when exwm--connection
|
|
|
|
|
(let ((original exwm-input--global-prefix-keys)
|
2016-02-03 05:12:24 +01:00
|
|
|
|
keysym keycode ungrab-key grab-key workspace)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(setq exwm-input--global-prefix-keys nil)
|
|
|
|
|
(dolist (i exwm-input--global-keys)
|
|
|
|
|
(cl-pushnew (elt i 0) exwm-input--global-prefix-keys))
|
2016-07-30 04:06:00 +02:00
|
|
|
|
;; Stop here if the global prefix keys are update-to-date and
|
|
|
|
|
;; there's no new workspace.
|
|
|
|
|
(unless (and (equal original exwm-input--global-prefix-keys)
|
|
|
|
|
(cl-every (lambda (w) (frame-parameter w 'exwm-grabbed))
|
|
|
|
|
exwm-workspace--list))
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(setq ungrab-key (make-instance 'xcb:UngrabKey
|
|
|
|
|
:key xcb:Grab:Any :grab-window nil
|
|
|
|
|
:modifiers xcb:ModMask:Any)
|
|
|
|
|
grab-key (make-instance 'xcb:GrabKey
|
|
|
|
|
:owner-events 0
|
|
|
|
|
:grab-window nil
|
|
|
|
|
:modifiers nil
|
|
|
|
|
:key nil
|
|
|
|
|
:pointer-mode xcb:GrabMode:Async
|
|
|
|
|
:keyboard-mode xcb:GrabMode:Async))
|
|
|
|
|
(dolist (w exwm-workspace--list)
|
|
|
|
|
(setq workspace (frame-parameter w 'exwm-workspace))
|
|
|
|
|
(setf (slot-value ungrab-key 'grab-window) workspace)
|
|
|
|
|
(if (xcb:+request-checked+request-check exwm--connection ungrab-key)
|
|
|
|
|
(exwm--log "Failed to ungrab keys")
|
2016-07-30 04:06:00 +02:00
|
|
|
|
;; Label this frame.
|
|
|
|
|
(set-frame-parameter w 'exwm-grabbed t)
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(dolist (k exwm-input--global-prefix-keys)
|
|
|
|
|
(setq keysym (xcb:keysyms:event->keysym exwm--connection k)
|
|
|
|
|
keycode (xcb:keysyms:keysym->keycode exwm--connection
|
|
|
|
|
(car keysym)))
|
|
|
|
|
(setf (slot-value grab-key 'grab-window) workspace
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(slot-value grab-key 'modifiers) (cdr keysym)
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(slot-value grab-key 'key) keycode)
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(when (or (= 0 keycode)
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(xcb:+request-checked+request-check exwm--connection
|
2016-11-13 12:23:10 +01:00
|
|
|
|
grab-key)
|
|
|
|
|
;; Also grab this key with num-lock mask set.
|
|
|
|
|
(when (/= 0 xcb:keysyms:num-lock-mask)
|
|
|
|
|
(setf (slot-value grab-key 'modifiers)
|
|
|
|
|
(logior (cdr keysym)
|
|
|
|
|
xcb:keysyms:num-lock-mask))
|
|
|
|
|
(xcb:+request-checked+request-check exwm--connection
|
|
|
|
|
grab-key)))
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(user-error "[EXWM] Failed to grab key: %s"
|
|
|
|
|
(single-key-description k))))))))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm-input-set-key (key command)
|
|
|
|
|
"Set a global key binding."
|
2016-02-19 10:12:43 +01:00
|
|
|
|
(interactive "KSet key globally: \nCSet key %s to command: ")
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(global-set-key key command)
|
2016-07-17 06:46:19 +02:00
|
|
|
|
(cl-pushnew key exwm-input--global-keys)
|
|
|
|
|
(when (called-interactively-p 'any)
|
|
|
|
|
(exwm-input--update-global-prefix-keys)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-07-15 02:11:33 +02:00
|
|
|
|
;; FIXME: Putting (t . EVENT) into `unread-command-events' does not really work
|
|
|
|
|
;; as documented in Emacs 24. Since inserting a conventional EVENT does
|
|
|
|
|
;; add it into (this-command-keys) there, we use `unread-command-events'
|
|
|
|
|
;; differently on Emacs 24 and 25.
|
|
|
|
|
(eval-and-compile
|
2016-07-25 06:14:26 +02:00
|
|
|
|
(if (< emacs-major-version 26)
|
2016-07-15 02:11:33 +02:00
|
|
|
|
(defsubst exwm-input--unread-event (event)
|
|
|
|
|
(setq unread-command-events
|
|
|
|
|
(append unread-command-events (list event))))
|
|
|
|
|
(defsubst exwm-input--unread-event (event)
|
|
|
|
|
(setq unread-command-events
|
|
|
|
|
(append unread-command-events `((t . ,event)))))))
|
|
|
|
|
|
2015-10-17 05:02:32 +02:00
|
|
|
|
(defvar exwm-input-command-whitelist nil
|
|
|
|
|
"A list of commands that when active all keys should be forwarded to Emacs.")
|
2016-02-06 05:59:33 +01:00
|
|
|
|
(make-obsolete-variable 'exwm-input-command-whitelist
|
|
|
|
|
"This variable can be safely removed." "25.1")
|
|
|
|
|
|
|
|
|
|
(defvar exwm-input--during-command nil
|
|
|
|
|
"Indicate whether between `pre-command-hook' and `post-command-hook'.")
|
2015-10-17 05:02:32 +02:00
|
|
|
|
|
2017-02-24 16:25:02 +01:00
|
|
|
|
(defvar exwm-input-line-mode-passthrough nil
|
2016-10-06 06:47:56 +02:00
|
|
|
|
"Non-nil makes 'line-mode' forwards all events to Emacs.")
|
|
|
|
|
|
|
|
|
|
(defvar exwm-input--line-mode-cache nil "Cache for incomplete key sequence.")
|
|
|
|
|
|
|
|
|
|
(defvar exwm-input--temp-line-mode nil
|
|
|
|
|
"Non-nil indicates it's in temporary line-mode for char-mode.")
|
|
|
|
|
|
|
|
|
|
(defun exwm-input--cache-event (event)
|
|
|
|
|
"Cache EVENT."
|
|
|
|
|
(setq exwm-input--line-mode-cache
|
|
|
|
|
(vconcat exwm-input--line-mode-cache (vector event)))
|
|
|
|
|
;; When the key sequence is complete.
|
|
|
|
|
(unless (keymapp (key-binding exwm-input--line-mode-cache))
|
|
|
|
|
(setq exwm-input--line-mode-cache nil)
|
|
|
|
|
(when exwm-input--temp-line-mode
|
|
|
|
|
(setq exwm-input--temp-line-mode nil)
|
|
|
|
|
(exwm-input--release-keyboard)))
|
|
|
|
|
(exwm-input--unread-event event))
|
|
|
|
|
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(defun exwm-input--on-KeyPress-line-mode (key-press raw-data)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"Parse X KeyPress event to Emacs key event and then feed the command loop."
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(with-slots (detail state) key-press
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state))
|
2016-10-06 06:47:56 +02:00
|
|
|
|
event mode)
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(when (and (/= 0 (car keysym))
|
|
|
|
|
(setq event (xcb:keysyms:keysym->event
|
|
|
|
|
exwm--connection (car keysym)
|
|
|
|
|
(logand state (lognot (cdr keysym)))))
|
2017-02-24 16:25:02 +01:00
|
|
|
|
(or exwm-input-line-mode-passthrough
|
2016-02-06 05:59:33 +01:00
|
|
|
|
exwm-input--during-command
|
2016-10-06 06:47:56 +02:00
|
|
|
|
;; Forward the event when there is an incomplete key
|
|
|
|
|
;; sequence or when the minibuffer is active.
|
|
|
|
|
exwm-input--line-mode-cache
|
|
|
|
|
(eq (active-minibuffer-window) (selected-window))
|
|
|
|
|
;;
|
2015-08-26 09:27:31 +02:00
|
|
|
|
(memq event exwm-input--global-prefix-keys)
|
|
|
|
|
(memq event exwm-input-prefix-keys)
|
|
|
|
|
(memq event exwm-input--simulation-prefix-keys)))
|
|
|
|
|
(setq mode xcb:Allow:AsyncKeyboard)
|
2016-10-06 06:47:56 +02:00
|
|
|
|
(exwm-input--cache-event event))
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(unless mode
|
|
|
|
|
(if (= 0 (logand #x6000 state)) ;Check the 13~14 bits.
|
|
|
|
|
;; Not an XKB state; just replay it.
|
|
|
|
|
(setq mode xcb:Allow:ReplayKeyboard)
|
|
|
|
|
;; An XKB state; sent it with SendEvent.
|
|
|
|
|
;; FIXME: Can this also be replayed?
|
|
|
|
|
;; FIXME: KeyRelease events are lost.
|
|
|
|
|
(setq mode xcb:Allow:AsyncKeyboard)
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:SendEvent
|
|
|
|
|
:propagate 0
|
|
|
|
|
:destination (slot-value key-press 'event)
|
|
|
|
|
:event-mask xcb:EventMask:NoEvent
|
2016-09-22 13:10:38 +02:00
|
|
|
|
:event raw-data)))
|
|
|
|
|
;; Make Emacs aware of this event when defining keyboard macros.
|
|
|
|
|
(when (and defining-kbd-macro event)
|
|
|
|
|
(set-transient-map '(keymap (t . (lambda () (interactive)))))
|
|
|
|
|
(exwm-input--unread-event event)))
|
2015-08-26 09:27:31 +02:00
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:AllowEvents
|
2016-07-29 11:05:09 +02:00
|
|
|
|
:mode mode
|
2015-08-26 09:27:31 +02:00
|
|
|
|
:time xcb:Time:CurrentTime))
|
|
|
|
|
(xcb:flush exwm--connection))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(defun exwm-input--on-KeyPress-char-mode (key-press &optional _raw-data)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"Handle KeyPress event in char-mode."
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(with-slots (detail state) key-press
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state))
|
|
|
|
|
event)
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(when (and (/= 0 (car keysym))
|
|
|
|
|
(setq event (xcb:keysyms:keysym->event
|
|
|
|
|
exwm--connection (car keysym)
|
|
|
|
|
(logand state (lognot (cdr keysym))))))
|
2015-08-07 06:41:15 +02:00
|
|
|
|
(when (eq major-mode 'exwm-mode)
|
2016-10-06 06:47:56 +02:00
|
|
|
|
(setq exwm-input--temp-line-mode t)
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(exwm-input--grab-keyboard)) ;grab keyboard temporarily
|
2016-10-06 06:47:56 +02:00
|
|
|
|
(exwm-input--cache-event event))))
|
2015-08-26 09:27:31 +02:00
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:AllowEvents
|
|
|
|
|
:mode xcb:Allow:AsyncKeyboard
|
|
|
|
|
:time xcb:Time:CurrentTime))
|
|
|
|
|
(xcb:flush exwm--connection))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-03-07 01:00:00 +01:00
|
|
|
|
(defun exwm-input--update-mode-line (id)
|
|
|
|
|
"Update the propertized `mode-line-process' for window ID."
|
|
|
|
|
(let (help-echo cmd mode)
|
2016-03-08 06:01:05 +01:00
|
|
|
|
(cl-case exwm--on-KeyPress
|
2016-03-07 01:00:00 +01:00
|
|
|
|
((exwm-input--on-KeyPress-line-mode)
|
|
|
|
|
(setq mode "line"
|
|
|
|
|
help-echo "mouse-1: Switch to char-mode"
|
|
|
|
|
cmd `(lambda ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(exwm-input-release-keyboard ,id))))
|
|
|
|
|
((exwm-input--on-KeyPress-char-mode)
|
|
|
|
|
(setq mode "char"
|
|
|
|
|
help-echo "mouse-1: Switch to line-mode"
|
|
|
|
|
cmd `(lambda ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(exwm-input-grab-keyboard ,id)))))
|
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
|
|
|
|
(setq mode-line-process
|
|
|
|
|
`(": "
|
|
|
|
|
(:propertize ,mode
|
|
|
|
|
help-echo ,help-echo
|
|
|
|
|
mouse-face mode-line-highlight
|
|
|
|
|
local-map
|
|
|
|
|
(keymap
|
|
|
|
|
(mode-line
|
|
|
|
|
keymap
|
|
|
|
|
(down-mouse-1 . ,cmd)))))))))
|
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input--grab-keyboard (&optional id)
|
|
|
|
|
"Grab all key events on window ID."
|
|
|
|
|
(unless id (setq id (exwm--buffer->id (window-buffer))))
|
2015-08-16 20:53:04 +02:00
|
|
|
|
(when id
|
|
|
|
|
(when (xcb:+request-checked+request-check exwm--connection
|
|
|
|
|
(make-instance 'xcb:GrabKey
|
2016-02-03 05:12:24 +01:00
|
|
|
|
:owner-events 0
|
2016-03-01 11:51:09 +01:00
|
|
|
|
:grab-window id
|
2015-08-16 20:53:04 +02:00
|
|
|
|
:modifiers xcb:ModMask:Any
|
|
|
|
|
:key xcb:Grab:Any
|
|
|
|
|
:pointer-mode xcb:GrabMode:Async
|
2015-08-26 09:27:31 +02:00
|
|
|
|
:keyboard-mode xcb:GrabMode:Sync))
|
2015-08-16 20:53:04 +02:00
|
|
|
|
(exwm--log "Failed to grab keyboard for #x%x" id))
|
2016-03-07 01:00:00 +01:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
|
|
|
|
(setq exwm--on-KeyPress #'exwm-input--on-KeyPress-line-mode))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm-input--release-keyboard (&optional id)
|
|
|
|
|
"Ungrab all key events on window ID."
|
|
|
|
|
(unless id (setq id (exwm--buffer->id (window-buffer))))
|
2015-08-16 20:53:04 +02:00
|
|
|
|
(when id
|
|
|
|
|
(when (xcb:+request-checked+request-check exwm--connection
|
|
|
|
|
(make-instance 'xcb:UngrabKey
|
2016-02-03 05:12:24 +01:00
|
|
|
|
:key xcb:Grab:Any
|
2016-03-01 11:51:09 +01:00
|
|
|
|
:grab-window id
|
2015-08-16 20:53:04 +02:00
|
|
|
|
:modifiers xcb:ModMask:Any))
|
|
|
|
|
(exwm--log "Failed to release keyboard for #x%x" id))
|
2016-03-07 01:00:00 +01:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
|
|
|
|
(setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;;;###autoload
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input-grab-keyboard (&optional id)
|
|
|
|
|
"Switch to line-mode."
|
2016-03-07 01:00:00 +01:00
|
|
|
|
(interactive (list (exwm--buffer->id (window-buffer))))
|
|
|
|
|
(when id
|
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
|
|
|
|
(exwm-input--grab-keyboard id)
|
2016-07-17 06:46:19 +02:00
|
|
|
|
(setq exwm--keyboard-grabbed t)
|
2016-03-07 01:00:00 +01:00
|
|
|
|
(exwm-input--update-mode-line id)
|
|
|
|
|
(force-mode-line-update))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;;;###autoload
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input-release-keyboard (&optional id)
|
|
|
|
|
"Switch to char-mode."
|
2016-03-07 01:00:00 +01:00
|
|
|
|
(interactive (list (exwm--buffer->id (window-buffer))))
|
|
|
|
|
(when id
|
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
|
|
|
|
(exwm-input--release-keyboard id)
|
2016-07-17 06:46:19 +02:00
|
|
|
|
(setq exwm--keyboard-grabbed nil)
|
2016-03-07 01:00:00 +01:00
|
|
|
|
(exwm-input--update-mode-line id)
|
|
|
|
|
(force-mode-line-update))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-08-24 13:21:17 +02:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun exwm-input-toggle-keyboard (&optional id)
|
|
|
|
|
"Toggle between 'line-mode' and 'char-mode'."
|
|
|
|
|
(interactive (list (exwm--buffer->id (window-buffer))))
|
|
|
|
|
(when id
|
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
|
|
|
|
(if exwm--keyboard-grabbed
|
|
|
|
|
(exwm-input-release-keyboard id)
|
|
|
|
|
(exwm-reset)))))
|
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input--fake-key (event)
|
|
|
|
|
"Fake a key event equivalent to Emacs event EVENT."
|
2015-10-30 10:23:02 +01:00
|
|
|
|
(let* ((keysym (xcb:keysyms:event->keysym exwm--connection event))
|
2015-10-28 13:38:06 +01:00
|
|
|
|
keycode id)
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(when (= 0 (car keysym))
|
2015-10-28 13:38:06 +01:00
|
|
|
|
(user-error "[EXWM] Invalid key: %s" (single-key-description event)))
|
|
|
|
|
(setq keycode (xcb:keysyms:keysym->keycode exwm--connection
|
|
|
|
|
(car keysym)))
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(when (/= 0 keycode)
|
2015-10-28 13:38:06 +01:00
|
|
|
|
(setq id (exwm--buffer->id (window-buffer (selected-window))))
|
2015-09-20 04:51:58 +02:00
|
|
|
|
(dolist (class '(xcb:KeyPress xcb:KeyRelease))
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:SendEvent
|
|
|
|
|
:propagate 0 :destination id
|
|
|
|
|
:event-mask xcb:EventMask:NoEvent
|
|
|
|
|
:event (xcb:marshal
|
|
|
|
|
(make-instance class
|
|
|
|
|
:detail keycode
|
|
|
|
|
:time xcb:Time:CurrentTime
|
|
|
|
|
:root exwm--root :event id
|
|
|
|
|
:child 0
|
|
|
|
|
:root-x 0 :root-y 0
|
|
|
|
|
:event-x 0 :event-y 0
|
2016-07-29 11:05:09 +02:00
|
|
|
|
:state (cdr keysym)
|
2015-09-20 04:51:58 +02:00
|
|
|
|
:same-screen 1)
|
|
|
|
|
exwm--connection)))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(xcb:flush exwm--connection)))
|
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;;;###autoload
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input-send-next-key (times)
|
|
|
|
|
"Send next key to client window."
|
|
|
|
|
(interactive "p")
|
|
|
|
|
(when (> times 12) (setq times 12))
|
|
|
|
|
(let (key keys)
|
|
|
|
|
(dotimes (i times)
|
|
|
|
|
;; Skip events not from keyboard
|
2017-02-24 16:25:02 +01:00
|
|
|
|
(let ((exwm-input-line-mode-passthrough t))
|
2017-02-19 23:53:43 +01:00
|
|
|
|
(catch 'break
|
|
|
|
|
(while t
|
|
|
|
|
(setq key (read-key (format "Send key: %s (%d/%d)"
|
|
|
|
|
(key-description keys)
|
|
|
|
|
(1+ i) times)))
|
|
|
|
|
(when (and (listp key) (eq (car key) t))
|
|
|
|
|
(setq key (cdr key)))
|
|
|
|
|
(unless (listp key) (throw 'break nil)))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(setq keys (vconcat keys (vector key)))
|
|
|
|
|
(exwm-input--fake-key key))))
|
|
|
|
|
|
|
|
|
|
;; (defun exwm-input-send-last-key ()
|
|
|
|
|
;; (interactive)
|
|
|
|
|
;; (unless (listp last-input-event) ;not a key event
|
|
|
|
|
;; (exwm-input--fake-key last-input-event)))
|
|
|
|
|
|
2016-06-17 12:45:34 +02:00
|
|
|
|
(defvar exwm-input--local-simulation-keys nil
|
|
|
|
|
"Whether simulation keys are local.")
|
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input--update-simulation-prefix-keys ()
|
|
|
|
|
"Update the list of prefix keys of simulation keys."
|
|
|
|
|
(setq exwm-input--simulation-prefix-keys nil)
|
|
|
|
|
(dolist (i exwm-input--simulation-keys)
|
2016-06-17 12:45:34 +02:00
|
|
|
|
(if exwm-input--local-simulation-keys
|
|
|
|
|
(local-set-key (car i) #'exwm-input-send-simulation-key)
|
|
|
|
|
(define-key exwm-mode-map (car i) #'exwm-input-send-simulation-key))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(cl-pushnew (elt (car i) 0) exwm-input--simulation-prefix-keys)))
|
|
|
|
|
|
|
|
|
|
(defun exwm-input-set-simulation-keys (simulation-keys)
|
|
|
|
|
"Set simulation keys.
|
|
|
|
|
|
2016-02-19 10:12:43 +01:00
|
|
|
|
SIMULATION-KEYS is an alist of the form (original-key . simulated-key)."
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(setq exwm-input--simulation-keys nil)
|
|
|
|
|
(dolist (i simulation-keys)
|
|
|
|
|
(cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys))
|
|
|
|
|
(exwm-input--update-simulation-prefix-keys))
|
|
|
|
|
|
2016-06-17 12:45:34 +02:00
|
|
|
|
(defun exwm-input-set-local-simulation-keys (simulation-keys)
|
|
|
|
|
"Set buffer-local simulation keys.
|
|
|
|
|
|
|
|
|
|
Its usage is the same with `exwm-input-set-simulation-keys'."
|
|
|
|
|
(make-local-variable 'exwm-input--simulation-keys)
|
|
|
|
|
(make-local-variable 'exwm-input--simulation-prefix-keys)
|
|
|
|
|
(use-local-map (copy-keymap exwm-mode-map))
|
|
|
|
|
(let ((exwm-input--local-simulation-keys t))
|
|
|
|
|
(exwm-input-set-simulation-keys simulation-keys)))
|
|
|
|
|
|
2016-02-19 10:12:43 +01:00
|
|
|
|
;;;###autoload
|
2015-08-05 08:10:44 +02:00
|
|
|
|
(defun exwm-input-send-simulation-key (times)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"Fake a key event according to last input key sequence."
|
2015-08-05 08:10:44 +02:00
|
|
|
|
(interactive "p")
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(when pair
|
|
|
|
|
(setq pair (cdr pair))
|
|
|
|
|
(unless (listp pair)
|
|
|
|
|
(setq pair (list pair)))
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(dotimes (_ times)
|
2015-08-05 08:10:44 +02:00
|
|
|
|
(dolist (j pair)
|
|
|
|
|
(exwm-input--fake-key j))))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-05-23 13:13:42 +02:00
|
|
|
|
(defun exwm-input--on-pre-command ()
|
|
|
|
|
"Run in `pre-command-hook'."
|
|
|
|
|
(setq exwm-input--during-command t))
|
|
|
|
|
|
|
|
|
|
(defun exwm-input--on-post-command ()
|
|
|
|
|
"Run in `post-command-hook'."
|
|
|
|
|
(setq exwm-input--during-command nil))
|
|
|
|
|
|
2016-02-19 10:12:43 +01:00
|
|
|
|
(declare-function exwm-floating--stop-moveresize "exwm-floating.el"
|
|
|
|
|
(&rest _args))
|
|
|
|
|
(declare-function exwm-floating--do-moveresize "exwm-floating.el"
|
|
|
|
|
(data _synthetic))
|
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-input--init ()
|
|
|
|
|
"Initialize the keyboard module."
|
|
|
|
|
;; Refresh keyboard mapping
|
|
|
|
|
(xcb:keysyms:init exwm--connection)
|
|
|
|
|
;; Convert move/resize buttons
|
2015-10-30 10:23:02 +01:00
|
|
|
|
(let ((move-key (xcb:keysyms:event->keysym exwm--connection
|
|
|
|
|
exwm-input-move-event))
|
|
|
|
|
(resize-key (xcb:keysyms:event->keysym exwm--connection
|
|
|
|
|
exwm-input-resize-event)))
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(when (= 0 (car move-key))
|
2015-10-28 13:38:06 +01:00
|
|
|
|
(user-error "[EXWM] Invalid key: %s"
|
|
|
|
|
(single-key-description exwm-input-move-event)))
|
2016-07-29 11:05:09 +02:00
|
|
|
|
(when (= 0 (car resize-key))
|
2015-10-28 13:38:06 +01:00
|
|
|
|
(user-error "[EXWM] Invalid key: %s"
|
|
|
|
|
(single-key-description exwm-input-resize-event)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(setq exwm-input--move-keysym (car move-key)
|
2016-07-29 11:05:09 +02:00
|
|
|
|
exwm-input--move-mask (cdr move-key)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
exwm-input--resize-keysym (car resize-key)
|
2016-07-29 11:05:09 +02:00
|
|
|
|
exwm-input--resize-mask (cdr resize-key)))
|
2016-08-09 07:34:29 +02:00
|
|
|
|
;; Create the X window and intern the atom used to fetch timestamp.
|
|
|
|
|
(setq exwm-input--timestamp-window (xcb:generate-id exwm--connection))
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:CreateWindow
|
|
|
|
|
:depth 0
|
|
|
|
|
:wid exwm-input--timestamp-window
|
|
|
|
|
:parent exwm--root
|
|
|
|
|
:x -1
|
|
|
|
|
:y -1
|
|
|
|
|
:width 1
|
|
|
|
|
:height 1
|
|
|
|
|
:border-width 0
|
|
|
|
|
:class xcb:WindowClass:CopyFromParent
|
|
|
|
|
:visual 0
|
|
|
|
|
:value-mask xcb:CW:EventMask
|
|
|
|
|
:event-mask xcb:EventMask:PropertyChange))
|
|
|
|
|
(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)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Attach event listeners
|
2016-08-09 07:34:29 +02:00
|
|
|
|
(xcb:+event exwm--connection 'xcb:PropertyNotify
|
|
|
|
|
#'exwm-input--on-PropertyNotify)
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress)
|
|
|
|
|
(xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(xcb:+event exwm--connection 'xcb:ButtonRelease
|
2015-09-04 03:09:59 +02:00
|
|
|
|
#'exwm-floating--stop-moveresize)
|
|
|
|
|
(xcb:+event exwm--connection 'xcb:MotionNotify
|
|
|
|
|
#'exwm-floating--do-moveresize)
|
2016-08-06 15:38:43 +02:00
|
|
|
|
(xcb:+event exwm--connection 'xcb:FocusIn #'exwm-input--on-FocusIn)
|
2016-07-26 15:42:22 +02:00
|
|
|
|
;; The input focus should be set on the frame when minibuffer is active.
|
|
|
|
|
(add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup)
|
2016-02-06 05:59:33 +01:00
|
|
|
|
;; Control `exwm-input--during-command'
|
2016-05-23 13:13:42 +02:00
|
|
|
|
(add-hook 'pre-command-hook #'exwm-input--on-pre-command)
|
|
|
|
|
(add-hook 'post-command-hook #'exwm-input--on-post-command)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Update focus when buffer list updates
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
|
2016-07-30 04:06:00 +02:00
|
|
|
|
;; Re-grab global keys.
|
|
|
|
|
(add-hook 'exwm-workspace-list-change-hook
|
2016-08-06 15:38:43 +02:00
|
|
|
|
#'exwm-input--on-workspace-list-change)
|
2017-01-02 17:14:33 +01:00
|
|
|
|
(exwm-input--on-workspace-list-change)
|
|
|
|
|
;; Prevent frame parameters introduced by this module from being
|
|
|
|
|
;; saved/restored.
|
|
|
|
|
(dolist (i '(exwm-grabbed))
|
|
|
|
|
(push (cons i :never) frameset-filter-alist)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-05-23 13:13:42 +02:00
|
|
|
|
(defun exwm-input--exit ()
|
|
|
|
|
"Exit the input module."
|
|
|
|
|
(remove-hook 'pre-command-hook #'exwm-input--on-pre-command)
|
|
|
|
|
(remove-hook 'post-command-hook #'exwm-input--on-post-command)
|
2016-07-30 04:06:00 +02:00
|
|
|
|
(remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
|
|
|
|
|
(remove-hook 'exwm-workspace-list-change-hook
|
2016-08-06 15:38:43 +02:00
|
|
|
|
#'exwm-input--on-workspace-list-change)
|
2016-07-30 13:01:33 +02:00
|
|
|
|
(when exwm-input--update-focus-defer-timer
|
|
|
|
|
(cancel-timer exwm-input--update-focus-defer-timer))
|
|
|
|
|
(when exwm-input--update-focus-timer
|
|
|
|
|
(cancel-timer exwm-input--update-focus-timer)))
|
2016-05-23 13:13:42 +02:00
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(provide 'exwm-input)
|
|
|
|
|
|
|
|
|
|
;;; exwm-input.el ends here
|