1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
|
;;; haiku-win.el --- set up windowing on Haiku -*- lexical-binding: t -*-
;; Copyright (C) 2021-2022 Free Software Foundation, Inc.
;; 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 <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Support for using Haiku's BeOS derived windowing system.
;;; Code:
(eval-when-compile (require 'cl-lib))
(unless (featurep 'haiku)
(error "%s: Loading haiku-win without having Haiku"
invocation-name))
;; Documentation-purposes only: actually loaded in loadup.el.
(require 'frame)
(require 'mouse)
(require 'scroll-bar)
(require 'menu-bar)
(require 'fontset)
(require 'dnd)
(add-to-list 'display-format-alist '(".*" . haiku))
;;;; Command line argument handling.
(defvar x-invocation-args)
(defvar x-command-line-resources)
(defvar haiku-initialized)
(defvar haiku-signal-invalid-refs)
(defvar haiku-drag-track-function)
(defvar haiku-dnd-selection-value nil
"The local value of the special `XdndSelection' selection.")
(defvar haiku-dnd-selection-converters '((STRING . haiku-dnd-convert-string)
(text/uri-list . haiku-dnd-convert-uri-list))
"Alist of X selection types to functions that act as selection converters.
The functions should accept a single argument VALUE, describing
the value of the drag-and-drop selection, and return a list of
two elements TYPE and DATA, where TYPE is a string containing the
MIME type of DATA, and DATA is a unibyte string, or nil if the
data could not be converted.
DATA can optionally have a text property `type', which specifies
the type of DATA inside the system message (see the doc string of
`haiku-drag-message' for more details).")
(defvar haiku-normal-selection-encoders '(haiku-select-encode-xstring
haiku-select-encode-utf-8-string)
"List of functions which act as selection encoders.
These functions accept two arguments SELECTION and VALUE, and
return an association appropriate for a serialized system
message (or nil if VALUE is not applicable to the encoder) that
will be put into the system selection SELECTION. VALUE is the
content that is being put into the selection by
`gui-set-selection'. See the doc string of `haiku-drag-message'
for more details on the structure of the associations.")
(defun haiku-selection-bounds (value)
"Return bounds of selection value VALUE.
The return value is a list (BEG END BUF) if VALUE is a cons of
two markers or an overlay. Otherwise, it is nil."
(cond ((bufferp value)
(with-current-buffer value
(when (mark t)
(list (mark t) (point) value))))
((and (consp value)
(markerp (car value))
(markerp (cdr value)))
(when (and (marker-buffer (car value))
(buffer-name (marker-buffer (car value)))
(eq (marker-buffer (car value))
(marker-buffer (cdr value))))
(list (marker-position (car value))
(marker-position (cdr value))
(marker-buffer (car value)))))
((overlayp value)
(when (overlay-buffer value)
(list (overlay-start value)
(overlay-end value)
(overlay-buffer value))))))
(defun haiku-dnd-convert-string (value)
"Convert VALUE to a UTF-8 string and appropriate MIME type.
Return a list of the appropriate MIME type, and UTF-8 data of
VALUE as a unibyte string, or nil if VALUE was not a string."
(unless (stringp value)
(when-let ((bounds (haiku-selection-bounds value)))
(setq value (ignore-errors
(with-current-buffer (nth 2 bounds)
(buffer-substring (nth 0 bounds)
(nth 1 bounds)))))))
(when (stringp value)
(list "text/plain" (string-to-unibyte
(encode-coding-string value 'utf-8)))))
(defun haiku-dnd-convert-uri-list (value)
"Convert VALUE to a file system reference if it is a file name."
(when (and (stringp value)
(file-exists-p value))
(list "refs" (propertize (expand-file-name value) 'type 'ref))))
(declare-function x-open-connection "haikufns.c")
(declare-function x-handle-args "common-win")
(declare-function haiku-selection-data "haikuselect.c")
(declare-function haiku-selection-put "haikuselect.c")
(declare-function haiku-selection-owner-p "haikuselect.c")
(declare-function haiku-put-resource "haikufns.c")
(declare-function haiku-drag-message "haikuselect.c")
(defun haiku--handle-x-command-line-resources (command-line-resources)
"Handle command line X resources specified with the option `-xrm'.
The resources should be a list of strings in COMMAND-LINE-RESOURCES."
(dolist (s command-line-resources)
(let ((components (split-string s ":")))
(when (car components)
(haiku-put-resource (car components)
(string-trim-left
(mapconcat #'identity (cdr components) ":")))))))
(cl-defmethod window-system-initialization (&context (window-system haiku)
&optional display)
"Set up the window system. WINDOW-SYSTEM must be HAIKU.
DISPLAY may be set to the name of a display that will be initialized."
(cl-assert (not haiku-initialized))
(create-default-fontset)
(when x-command-line-resources
(haiku--handle-x-command-line-resources
(split-string x-command-line-resources "\n")))
(x-open-connection (or display "be") x-command-line-resources t)
(setq haiku-initialized t))
(cl-defmethod frame-creation-function (params &context (window-system haiku))
(x-create-frame-with-faces params))
(cl-defmethod handle-args-function (args &context (window-system haiku))
(x-handle-args args))
(defun haiku--selection-type-to-mime (type)
"Convert symbolic selection type TYPE to its MIME equivalent.
If TYPE is nil, return \"text/plain\"."
(cond
((eq type 'STRING) "text/plain;charset=iso-8859-1")
((eq type 'UTF8_STRING) "text/plain")
((stringp type) type)
((symbolp type) (symbol-name type))
(t "text/plain")))
(defun haiku-selection-targets (clipboard)
"Find the types of data available from CLIPBOARD.
CLIPBOARD should be the symbol `PRIMARY', `SECONDARY' or
`CLIPBOARD'. Return the available types as a list of strings."
(mapcar #'car (haiku-selection-data clipboard nil)))
(defun haiku-select-encode-xstring (_selection value)
"Convert VALUE to a system message association.
VALUE will be encoded as Latin-1 (like on X Windows) and stored
under the type `text/plain;charset=iso-8859-1'."
(unless (stringp value)
(when-let ((bounds (haiku-selection-bounds value)))
(setq value (ignore-errors
(with-current-buffer (nth 2 bounds)
(buffer-substring (nth 0 bounds)
(nth 1 bounds)))))))
(when (and (stringp value) (not (string-empty-p value)))
(list "text/plain;charset=iso-8859-1" 1296649541
(encode-coding-string value 'iso-latin-1))))
(defun haiku-select-encode-utf-8-string (_selection value)
"Convert VALUE to a system message association.
VALUE will be encoded as UTF-8 and stored under the type
`text/plain'."
(unless (stringp value)
(when-let ((bounds (haiku-selection-bounds value)))
(setq value (ignore-errors
(with-current-buffer (nth 2 bounds)
(buffer-substring (nth 0 bounds)
(nth 1 bounds)))))))
(when (and (stringp value) (not (string-empty-p value)))
(list "text/plain" 1296649541
(encode-coding-string value 'utf-8-unix))))
(cl-defmethod gui-backend-get-selection (type data-type
&context (window-system haiku))
(if (eq data-type 'TARGETS)
(apply #'vector (mapcar #'intern
(haiku-selection-targets type)))
(if (eq type 'XdndSelection)
haiku-dnd-selection-value
(haiku-selection-data type (haiku--selection-type-to-mime data-type)))))
(cl-defmethod gui-backend-set-selection (type value
&context (window-system haiku))
(if (eq type 'XdndSelection)
(setq haiku-dnd-selection-value value)
(let ((message nil))
(dolist (encoder haiku-normal-selection-encoders)
(let ((result (funcall encoder type value)))
(when result
(push result message))))
(haiku-selection-put type message))))
(cl-defmethod gui-backend-selection-exists-p (selection
&context (window-system haiku))
(haiku-selection-data selection "text/plain"))
(cl-defmethod gui-backend-selection-owner-p (selection &context (window-system haiku))
(haiku-selection-owner-p selection))
(declare-function haiku-read-file-name "haikufns.c")
(defun x-file-dialog (prompt dir &optional default-filename mustmatch only-dir-p)
"SKIP: real doc in xfns.c."
(if (eq (framep-on-display (selected-frame)) 'haiku)
(haiku-read-file-name (if (not (string-suffix-p ": " prompt))
prompt
(substring prompt 0 (- (length prompt) 2)))
(selected-frame)
(or dir (and default-filename
(file-name-directory default-filename)))
mustmatch only-dir-p
(file-name-nondirectory default-filename))
(error "x-file-dialog on a tty frame")))
(defun haiku-drag-and-drop (event)
"Handle specified drag-n-drop EVENT."
(interactive "e")
(let* ((string (caddr event))
(window (posn-window (event-start event))))
(if (eq string 'lambda) ; This means the mouse moved.
(dnd-handle-movement (event-start event))
(cond
((assoc "refs" string)
(with-selected-window window
(raise-frame)
(dolist (filename (cddr (assoc "refs" string)))
(dnd-handle-one-url window 'private
(concat "file:" filename)))))
((assoc "text/plain" string)
(with-selected-window window
(raise-frame)
(dolist (text (cddr (assoc "text/plain" string)))
(goto-char (posn-point (event-start event)))
(dnd-insert-text window 'private
(if (multibyte-string-p text)
text
(decode-coding-string text 'undecided))))))
((not (eq (cdr (assq 'type string))
3003)) ; Type of the placeholder message Emacs uses
; to cancel a drop on C-g.
(message "Don't know how to drop any of: %s"
(mapcar #'car string)))))))
(define-key special-event-map [drag-n-drop]
'haiku-drag-and-drop)
(defvaralias 'haiku-use-system-tooltips 'use-system-tooltips)
(defun haiku-use-system-tooltips-watcher (&rest _ignored)
"Variable watcher to force a menu bar update when `use-system-tooltip' changes.
This is necessary because on Haiku `use-system-tooltip' doesn't
take effect on menu items until the menu bar is updated again."
(force-mode-line-update t))
;; Note that `mouse-position' can't return the actual frame the mouse
;; pointer is under, so this only works for the frame where the drop
;; started.
(defun haiku-dnd-drag-handler ()
"Handle mouse movement during drag-and-drop."
(let ((track-mouse 'drag-source)
(mouse-position (mouse-pixel-position)))
(when (car mouse-position)
(dnd-handle-movement (posn-at-x-y (cadr mouse-position)
(cddr mouse-position)
(car mouse-position)))
(redisplay))))
(setq haiku-drag-track-function #'haiku-dnd-drag-handler)
(defun x-begin-drag (targets &optional action frame _return-frame allow-current-frame)
"SKIP: real doc in xfns.c."
(unless haiku-dnd-selection-value
(error "No local value for XdndSelection"))
(let ((message nil)
(mouse-highlight nil)
(haiku-signal-invalid-refs nil))
(dolist (target targets)
(let ((selection-converter (cdr (assoc (intern target)
haiku-dnd-selection-converters))))
(when selection-converter
(let ((selection-result
(funcall selection-converter
haiku-dnd-selection-value)))
(when selection-result
(let ((field (cdr (assoc (car selection-result) message))))
(unless (cadr field)
;; Add B_MIME_TYPE to the message if the type was not
;; previously specified, or the type if it was.
(push (or (get-text-property 0 'type
(cadr selection-result))
1296649541)
(alist-get (car selection-result) message
nil nil #'equal))))
(push (cadr selection-result)
(cdr (alist-get (car selection-result) message
nil nil #'equal))))))))
(prog1 (or (and (symbolp action)
action)
'XdndActionCopy)
(haiku-drag-message (or frame (selected-frame))
message allow-current-frame))))
(add-variable-watcher 'use-system-tooltips #'haiku-use-system-tooltips-watcher)
(provide 'haiku-win)
(provide 'term/haiku-win)
;;; haiku-win.el ends here
|