Compare commits

...

4 Commits

Author SHA1 Message Date
TEC 2109b78cca
Introduce an ee-app-info-function customisation 2024-04-16 11:54:08 +08:00
TEC bdc0ec3ff4
Refactor linux app-info functions 2024-04-16 11:48:13 +08:00
TEC 08e5101045
Make the windows ID always a string
With some Wayland DEs/compositors, the APP id isn't always a base 10
number, so let's just leave it as a string instead of parsing it then
making it a string again.
2024-04-16 11:35:09 +08:00
TEC 98de03162e
Make checkdoc and the byte-compiler happier 2024-04-16 11:14:19 +08:00
1 changed files with 87 additions and 69 deletions

View File

@ -189,7 +189,7 @@ Set to nil to disable."
(defcustom emacs-everywhere-file-dir
temporary-file-directory
"The default directory for temp files generated by `emacs-everywhere-filename-function'."
"The default dir for`emacs-everywhere-filename-function'-generated temp files."
:type 'string
:group 'emacs-everywhere)
@ -214,7 +214,7 @@ Set to nil to disable."
:group 'emacs-everywhere)
(defun emacs-everywhere-temp-filename (app-info)
"Generate a temp file."
"Generate a temp file based on APP-INFO."
(concat "emacs-everywhere-"
(format-time-string "%Y%m%d-%H%M%S-" (current-time))
(emacs-everywhere-app-class app-info)))
@ -227,6 +227,20 @@ Make sure that it will be matched by `emacs-everywhere-file-patterns'."
:type 'function
:group 'emacs-everywhere)
(defcustom emacs-everywhere-app-info-function
(pcase emacs-everywhere--display-server
(`(quartz . ,_) (emacs-everywhere--app-info-osx))
(`(windows . ,_) (emacs-everywhere--app-info-windows))
(`(x11 . ,_) (emacs-everywhere--app-info-linux-x11))
(`(wayland . KDE) (emacs-everywhere--app-info-linux-kde)))
"Function that asks the system for information on the current foreground app.
On most systems, this should be set to a sensible default, but it
may not be set on less common configurations. If unset, a custom
app-info function can be used see the various
emacs-everywhere--app-info-* functions for reference."
:type 'function
:group 'emacs-everywhere)
;; Semi-internal variables
(defconst emacs-everywhere-osascript-accessibility-error-message
@ -234,7 +248,7 @@ Make sure that it will be matched by `emacs-everywhere-file-patterns'."
"String to search for to determine if Emacs does not have accessibility rights.")
(defvar-local emacs-everywhere-current-app nil
"The current `emacs-everywhere-app'")
"The current `emacs-everywhere-app'.")
;; Prevents buffer-local variable from being unset by major mode changes
(put 'emacs-everywhere-current-app 'permanent-local t)
@ -248,12 +262,14 @@ Make sure that it will be matched by `emacs-everywhere-file-patterns'."
(declare-function evil-insert-state "evil-states")
(declare-function spell-fu-buffer "spell-fu")
(declare-function markdown-mode "markdown-mode")
(declare-function w32-shell-execute "w32fns.c")
;;; Primary functionality
;;;###autoload
(defun emacs-everywhere (&optional file line column)
"Launch the emacs-everywhere frame from emacsclient."
"Launch the emacs-everywhere frame from emacsclient.
This may open FILE at a particular LINE and COLUMN, if specified."
(let* ((app-info (emacs-everywhere-app-info))
(param (emacs-everywhere-command-param app-info file line column))
(param-string (combine-and-quote-strings param)))
@ -263,7 +279,9 @@ Make sure that it will be matched by `emacs-everywhere-file-patterns'."
(_ (apply #'call-process "emacsclient" nil 0 nil param)))))
(defun emacs-everywhere-command-param (app-info &optional file line column)
"Generate arguments for calling emacsclient."
"Generate arguments for calling emacsclient.
The arguments are based on a particular APP-INFO. Optionally, a FILE can be
specified, and also a particular LINE and COLUMN."
(delq
nil (list
(when (server-running-p)
@ -277,8 +295,7 @@ Make sure that it will be matched by `emacs-everywhere-file-patterns'."
(if (memq system-type '(ms-dos windows-nt cygwin))
(expand-file-name server-name server-auth-dir)
(shell-quote-argument
(expand-file-name server-name server-socket-dir)))
)))
(expand-file-name server-name server-socket-dir))))))
"-c" "-F"
(prin1-to-string
(cons (cons 'emacs-everywhere-app app-info)
@ -363,7 +380,7 @@ buffers.")
(delete-region (point-min) (point-max)))
(defun emacs-everywhere--finish-or-ctrl-c-ctrl-c ()
"Finish emacs-everywhere session or invoke `org-ctrl-c-ctrl-c' in org-mode."
"Finish emacs-everywhere session or invoke `org-ctrl-c-ctrl-c' in `org-mode'."
(interactive)
(if (and (eq major-mode 'org-mode)
(org-in-src-block-p))
@ -394,12 +411,11 @@ Never paste content when ABORT is non-nil."
(cdr emacs-everywhere-copy-command))))))
(sleep-for emacs-everywhere-clipboard-sleep-delay) ; prevents weird multi-second pause, lets clipboard info propagate
(when emacs-everywhere-window-focus-command
(let* ((window-id (emacs-everywhere-app-id emacs-everywhere-current-app))
(window-id-str (if (numberp window-id) (number-to-string window-id) window-id)))
(let ((window-id (emacs-everywhere-app-id emacs-everywhere-current-app)))
(apply #'call-process (car emacs-everywhere-window-focus-command)
nil nil nil
(mapcar (lambda (arg)
(replace-regexp-in-string "%w" window-id-str arg))
(replace-regexp-in-string "%w" window-id arg))
(cdr emacs-everywhere-window-focus-command)))
;; The frame only has this parameter if this package initialized the temp
;; file its displaying. Otherwise, it was created by another program, likely
@ -427,20 +443,19 @@ Never paste content when ABORT is non-nil."
id class title geometry)
(defun emacs-everywhere-app-info ()
"Return information on the active window."
(let ((w (pcase system-type
(`darwin (emacs-everywhere--app-info-osx))
((or `ms-dos `windows-nt `cygwin)
(emacs-everywhere--app-info-windows))
(_ (emacs-everywhere--app-info-linux)))))
(setf (emacs-everywhere-app-title w)
(replace-regexp-in-string
(format " ?-[A-Za-z0-9 ]*%s"
(regexp-quote (emacs-everywhere-app-class w)))
""
(replace-regexp-in-string
"[^[:ascii:]]+" "-" (emacs-everywhere-app-title w))))
w))
"Return information on the active window.
This runs `emacs-everywhere-app-info-function' and lightly reformats the app title."
(if (functionp emacs-everywhere-app-info-function)
(let ((w (funcall emacs-everywhere-app-info-function)))
(setf (emacs-everywhere-app-title w)
(replace-regexp-in-string
(format " ?-[A-Za-z0-9 ]*%s"
(regexp-quote (emacs-everywhere-app-class w)))
""
(replace-regexp-in-string
"[^[:ascii:]]+" "-" (emacs-everywhere-app-title w))))
w))
(user-error "No app-info function is set, see `emacs-everywhere-app-info-function'"))
(defun emacs-everywhere--call (command &rest args)
"Execute COMMAND with ARGS synchronously."
@ -451,29 +466,15 @@ Never paste content when ABORT is non-nil."
(call-process "osascript" nil nil nil
"-e" (format "display alert \"emacs-everywhere\" message \"Emacs has not been granted accessibility permissions, cannot run emacs-everywhere!
Please go to 'System Preferences > Security & Privacy > Privacy > Accessibility' and allow Emacs.\"" ))
(error "MacOS accessibility error, aborting."))
(error "MacOS accessibility error, aborting"))
(string-trim (buffer-string))))
(defun emacs-everywhere--app-info-linux ()
"Return information on the active window, on linux."
(pcase-let ((`(,window-id ,app-name ,window-title ,window-geometry)
(pcase emacs-everywhere--display-server
(`(x11 . ,_) (emacs-everywhere--app-info-linux-x11))
(`(wayland . KDE) (emacs-everywhere--app-info-linux-kde))
(_ (user-error "Unable to fetch app info with display server %S" emacs-everywhere--display-server)))))
(make-emacs-everywhere-app
:id (string-to-number window-id)
:class app-name
:title window-title
:geometry (list
(if (= (nth 0 window-geometry) (nth 2 window-geometry))
(nth 0 window-geometry)
(- (nth 0 window-geometry) (nth 2 window-geometry)))
(if (= (nth 1 window-geometry) (nth 3 window-geometry))
(nth 1 window-geometry)
(- (nth 1 window-geometry) (nth 3 window-geometry)))
(nth 4 window-geometry)
(nth 5 window-geometry)))))
"Return information on the active window, on Linux."
(pcase emacs-everywhere--display-server
(`(x11 . ,_) (emacs-everywhere--app-info-linux-x11))
(`(wayland . KDE) (emacs-everywhere--app-info-linux-kde))
(_ (user-error "Unable to fetch app info with display server %S" emacs-everywhere--display-server))))
(defun emacs-everywhere--app-info-linux-x11 ()
"Return information on the current active window, on a Linux X11 sessions."
@ -500,7 +501,21 @@ Please go to 'System Preferences > Security & Privacy > Privacy > Accessibility'
(cadr (assoc "Relative upper-left Y" info))
(cadr (assoc "Width" info))
(cadr (assoc "Height" info)))))))
(list window-id app-name window-title window-geometry))))
(setq window-geometry
(list
(if (= (nth 0 window-geometry) (nth 2 window-geometry))
(nth 0 window-geometry)
(- (nth 0 window-geometry) (nth 2 window-geometry)))
(if (= (nth 1 window-geometry) (nth 3 window-geometry))
(nth 1 window-geometry)
(- (nth 1 window-geometry) (nth 3 window-geometry)))
(nth 4 window-geometry)
(nth 5 window-geometry)))
(make-emacs-everywhere-app
:id window-id
:class app-name
:title window-title
:geometry window-geometry))))
(defun emacs-everywhere--app-info-linux-kde ()
"Return information on the current active window, on a Linux KDE sessions."
@ -518,10 +533,13 @@ Please go to 'System Preferences > Security & Privacy > Privacy > Accessibility'
(mapcar #'string-to-number
(list (cadr (assoc "Position" geom))
(caddr (assoc "Position" geom))
"0" "0"
(cadr (assoc "Geometry" geom))
(caddr (assoc "Geometry" geom)))))))
(list window-id app-name window-title window-geometry))))
(make-emacs-everywhere-app
:id window-id
:class app-name
:title window-title
:geometry window-geometry))))
(defvar emacs-everywhere--dir (file-name-directory load-file-name))
@ -583,27 +601,27 @@ return windowTitle"))
(defun emacs-everywhere--app-info-windows ()
"Return information on the active window, on Windows."
(let ((window-id (emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
"& {Add-Type 'using System; using System.Runtime.InteropServices; public class Tricks { [DllImport(\"user32.dll\")] public static extern IntPtr GetForegroundWindow(); }'; [tricks]::GetForegroundWindow() }"))
(window-title (emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
(format "& {Add-Type 'using System; using System.Runtime.InteropServices; public class Tricks { [DllImport(\"user32.dll\")] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count); [DllImport(\"user32.dll\")] public static extern int GetWindowTextLength(IntPtr hWnd); }'; $length = ([tricks]::GetWindowTextLength(%s)); $sb = New-Object System.Text.StringBuilder $length; [tricks]::GetWindowText(%s, $sb, $length + 1) > $null; $sb.ToString() }" window-id window-id)))
(window-class (emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
(format "(Get-Item (Get-Process | ? { $_.mainwindowhandle -eq %s }).Path).VersionInfo.ProductName" window-id)))
(window-geometry (split-string
(emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
(format "& {Add-Type 'using System; using System.Runtime.InteropServices; public struct tagRECT { public int left; public int top; public int right; public int bottom; } public class Tricks { [DllImport(\"user32.dll\")] public static extern int GetWindowRect(IntPtr hWnd, out tagRECT lpRect); }'; $rect = New-Object -TypeName tagRECT; [tricks]::GetWindowRect(%s, [ref]$rect) > $null; $rect.left; $rect.top; $rect.right - $rect.left; $rect.bottom - $rect.top }" window-id)))))
(let* ((window-id (emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
"& {Add-Type 'using System; using System.Runtime.InteropServices; public class Tricks { [DllImport(\"user32.dll\")] public static extern IntPtr GetForegroundWindow(); }'; [tricks]::GetForegroundWindow() }"))
(window-title (emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
(format "& {Add-Type 'using System; using System.Runtime.InteropServices; public class Tricks { [DllImport(\"user32.dll\")] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count); [DllImport(\"user32.dll\")] public static extern int GetWindowTextLength(IntPtr hWnd); }'; $length = ([tricks]::GetWindowTextLength(%s)); $sb = New-Object System.Text.StringBuilder $length; [tricks]::GetWindowText(%s, $sb, $length + 1) > $null; $sb.ToString() }" window-id window-id)))
(window-class (emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
(format "(Get-Item (Get-Process | ? { $_.mainwindowhandle -eq %s }).Path).VersionInfo.ProductName" window-id)))
(window-geometry (split-string
(emacs-everywhere--call
"powershell"
"-NoProfile"
"-Command"
(format "& {Add-Type 'using System; using System.Runtime.InteropServices; public struct tagRECT { public int left; public int top; public int right; public int bottom; } public class Tricks { [DllImport(\"user32.dll\")] public static extern int GetWindowRect(IntPtr hWnd, out tagRECT lpRect); }'; $rect = New-Object -TypeName tagRECT; [tricks]::GetWindowRect(%s, [ref]$rect) > $null; $rect.left; $rect.top; $rect.right - $rect.left; $rect.bottom - $rect.top }" window-id)))))
(make-emacs-everywhere-app
:id window-id
:class window-class