aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2022-03-17 03:42:19 +0000
committerPo Lu2022-03-17 03:42:19 +0000
commit00172ae0c8a3087578f6e91251f887f6b7b4f682 (patch)
tree5ef294107d444a408a12af1000411b541145e1a9 /src
parentc223e2aefcabc7ad29c4be186fc07825bbcce196 (diff)
downloademacs-00172ae0c8a3087578f6e91251f887f6b7b4f682.tar.gz
emacs-00172ae0c8a3087578f6e91251f887f6b7b4f682.zip
Implement cross-program drag-and-drop on Haiku
* doc/lispref/frames.texi (Drag and Drop): Fix documentation of `x-begin-drag' to match actual function arity. * lisp/term/haiku-win.el (haiku-dnd-selection-value): New variable. (haiku-dnd-selection-converters): New variable. (haiku-dnd-convert-string): New function. (gui-backend-get-selection, gui-backend-set-selection): Handle XdndSelection specially. (x-begin-drag): New function. * src/haiku_select.cc (be_create_simple_message) (be_add_message_data): New functions. * src/haiku_support.cc (WAIT_FOR_RELEASE): New message type. (class EmacsView, MouseUp): If waiting for release, reply and drop event. (be_drag_message, be_drag_message_thread_entry): New functions. * src/haiku_support.h: Update prototypes. * src/haikuselect.c (lisp_to_type_code, haiku_lisp_to_message) (Fhaiku_drag_message): New functions. (syms_of_haikuselect): Define new subr. * src/haikuselect.h: Update prototypes.
Diffstat (limited to 'src')
-rw-r--r--src/haiku_select.cc16
-rw-r--r--src/haiku_support.cc112
-rw-r--r--src/haiku_support.h6
-rw-r--r--src/haikuselect.c224
-rw-r--r--src/haikuselect.h4
5 files changed, 358 insertions, 4 deletions
diff --git a/src/haiku_select.cc b/src/haiku_select.cc
index abb07b20028..4212f60a480 100644
--- a/src/haiku_select.cc
+++ b/src/haiku_select.cc
@@ -321,3 +321,19 @@ be_get_message_data (void *message, const char *name,
321 return msg->FindData (name, type_code, 321 return msg->FindData (name, type_code,
322 index, buf_return, size_return) != B_OK; 322 index, buf_return, size_return) != B_OK;
323} 323}
324
325void *
326be_create_simple_message (void)
327{
328 return new BMessage (B_SIMPLE_DATA);
329}
330
331int
332be_add_message_data (void *message, const char *name,
333 int32 type_code, const void *buf,
334 ssize_t buf_size)
335{
336 BMessage *msg = (BMessage *) message;
337
338 return msg->AddData (name, type_code, buf, buf_size) != B_OK;
339}
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
index 884e3583e25..626b2fb607b 100644
--- a/src/haiku_support.cc
+++ b/src/haiku_support.cc
@@ -81,6 +81,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
81#include "haiku_support.h" 81#include "haiku_support.h"
82 82
83#define SCROLL_BAR_UPDATE 3000 83#define SCROLL_BAR_UPDATE 3000
84#define WAIT_FOR_RELEASE 3001
84 85
85static color_space dpy_color_space = B_NO_COLOR_SPACE; 86static color_space dpy_color_space = B_NO_COLOR_SPACE;
86static key_map *key_map = NULL; 87static key_map *key_map = NULL;
@@ -1177,6 +1178,7 @@ public:
1177#endif 1178#endif
1178 1179
1179 BPoint tt_absl_pos; 1180 BPoint tt_absl_pos;
1181 BMessage *wait_for_release_message = NULL;
1180 1182
1181 color_space cspace; 1183 color_space cspace;
1182 1184
@@ -1187,6 +1189,9 @@ public:
1187 1189
1188 ~EmacsView () 1190 ~EmacsView ()
1189 { 1191 {
1192 if (wait_for_release_message)
1193 gui_abort ("Wait for release message still exists");
1194
1190 TearDownDoubleBuffering (); 1195 TearDownDoubleBuffering ();
1191 } 1196 }
1192 1197
@@ -1196,6 +1201,28 @@ public:
1196 cspace = B_RGBA32; 1201 cspace = B_RGBA32;
1197 } 1202 }
1198 1203
1204 void
1205 MessageReceived (BMessage *msg)
1206 {
1207 uint32 buttons;
1208 BLooper *looper = Looper ();
1209
1210 if (msg->what == WAIT_FOR_RELEASE)
1211 {
1212 if (wait_for_release_message)
1213 gui_abort ("Wait for release message already exists");
1214
1215 GetMouse (NULL, &buttons, false);
1216
1217 if (!buttons)
1218 msg->SendReply (msg);
1219 else
1220 wait_for_release_message = looper->DetachCurrentMessage ();
1221 }
1222 else
1223 BView::MessageReceived (msg);
1224 }
1225
1199#ifdef USE_BE_CAIRO 1226#ifdef USE_BE_CAIRO
1200 void 1227 void
1201 DetachCairoSurface (void) 1228 DetachCairoSurface (void)
@@ -1483,6 +1510,16 @@ public:
1483 1510
1484 this->GetMouse (&point, &buttons, false); 1511 this->GetMouse (&point, &buttons, false);
1485 1512
1513 if (!buttons && wait_for_release_message)
1514 {
1515 wait_for_release_message->SendReply (wait_for_release_message);
1516 delete wait_for_release_message;
1517 wait_for_release_message = NULL;
1518
1519 previous_buttons = buttons;
1520 return;
1521 }
1522
1486 rq.window = this->Window (); 1523 rq.window = this->Window ();
1487 1524
1488 if ((previous_buttons & B_PRIMARY_MOUSE_BUTTON) 1525 if ((previous_buttons & B_PRIMARY_MOUSE_BUTTON)
@@ -3870,3 +3907,78 @@ BMessage_delete (void *message)
3870{ 3907{
3871 delete (BMessage *) message; 3908 delete (BMessage *) message;
3872} 3909}
3910
3911static int32
3912be_drag_message_thread_entry (void *thread_data)
3913{
3914 BMessenger *messenger;
3915 BMessage reply;
3916
3917 messenger = (BMessenger *) thread_data;
3918 messenger->SendMessage (WAIT_FOR_RELEASE, &reply);
3919
3920 return 0;
3921}
3922
3923void
3924be_drag_message (void *view, void *message,
3925 void (*block_input_function) (void),
3926 void (*unblock_input_function) (void),
3927 void (*process_pending_signals_function) (void))
3928{
3929 EmacsView *vw = (EmacsView *) view;
3930 BMessage *msg = (BMessage *) message;
3931 BMessage wait_for_release;
3932 BMessenger messenger (vw);
3933 struct object_wait_info infos[2];
3934 ssize_t stat;
3935
3936 block_input_function ();
3937 if (!vw->LockLooper ())
3938 gui_abort ("Failed to lock view looper for drag");
3939
3940 vw->DragMessage (msg, BRect (0, 0, 0, 0));
3941 vw->UnlockLooper ();
3942
3943 infos[0].object = port_application_to_emacs;
3944 infos[0].type = B_OBJECT_TYPE_PORT;
3945 infos[0].events = B_EVENT_READ;
3946
3947 infos[1].object = spawn_thread (be_drag_message_thread_entry,
3948 "Drag waiter thread",
3949 B_DEFAULT_MEDIA_PRIORITY,
3950 (void *) &messenger);
3951 infos[1].type = B_OBJECT_TYPE_THREAD;
3952 infos[1].events = B_EVENT_INVALID;
3953 unblock_input_function ();
3954
3955 if (infos[1].object < B_OK)
3956 return;
3957
3958 block_input_function ();
3959 resume_thread (infos[1].object);
3960 unblock_input_function ();
3961
3962 while (true)
3963 {
3964 block_input_function ();
3965 stat = wait_for_objects ((struct object_wait_info *) &infos, 2);
3966 unblock_input_function ();
3967
3968 if (stat == B_INTERRUPTED || stat == B_TIMED_OUT
3969 || stat == B_WOULD_BLOCK)
3970 continue;
3971
3972 if (stat < B_OK)
3973 gui_abort ("Failed to wait for drag");
3974
3975 if (infos[0].events & B_EVENT_READ)
3976 process_pending_signals_function ();
3977
3978 if (infos[1].events & B_EVENT_INVALID)
3979 return;
3980
3981 infos[0].events = B_EVENT_READ;
3982 infos[1].events = B_EVENT_INVALID;
3983 }
3984}
diff --git a/src/haiku_support.h b/src/haiku_support.h
index 78d51b83d8b..af7216286a7 100644
--- a/src/haiku_support.h
+++ b/src/haiku_support.h
@@ -945,6 +945,12 @@ extern "C"
945 extern void 945 extern void
946 BMessage_delete (void *message); 946 BMessage_delete (void *message);
947 947
948 extern void
949 be_drag_message (void *view, void *message,
950 void (*block_input_function) (void),
951 void (*unblock_input_function) (void),
952 void (*process_pending_signals_function) (void));
953
948#ifdef __cplusplus 954#ifdef __cplusplus
949 extern void * 955 extern void *
950 find_appropriate_view_for_draw (void *vw); 956 find_appropriate_view_for_draw (void *vw);
diff --git a/src/haikuselect.c b/src/haikuselect.c
index f291fa70edd..322e01f7918 100644
--- a/src/haikuselect.c
+++ b/src/haikuselect.c
@@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
23#include "coding.h" 23#include "coding.h"
24#include "haikuselect.h" 24#include "haikuselect.h"
25#include "haikuterm.h" 25#include "haikuterm.h"
26#include "haiku_support.h"
26 27
27#include <stdlib.h> 28#include <stdlib.h>
28 29
@@ -181,10 +182,10 @@ same as `SECONDARY'. */)
181 182
182/* Return the Lisp representation of MESSAGE. 183/* Return the Lisp representation of MESSAGE.
183 184
184 It is an alist of strings, denoting message parameter names, to a 185 It is an alist of strings, denoting message field names, to a list
185 list the form (TYPE . (DATA ...)), where TYPE is an integer 186 of the form (TYPE DATA ...), where TYPE is an integer denoting the
186 denoting the system data type of DATA, and DATA is in the general 187 system data type of DATA, and DATA is in the general case a unibyte
187 case a unibyte string. 188 string.
188 189
189 If TYPE is a symbol instead of an integer, then DATA was specially 190 If TYPE is a symbol instead of an integer, then DATA was specially
190 decoded. If TYPE is `ref', then DATA is the absolute file name of 191 decoded. If TYPE is `ref', then DATA is the absolute file name of
@@ -311,6 +312,220 @@ haiku_message_to_lisp (void *message)
311 return list; 312 return list;
312} 313}
313 314
315static int32
316lisp_to_type_code (Lisp_Object obj)
317{
318 if (BIGNUMP (obj))
319 return (int32) bignum_to_intmax (obj);
320
321 if (FIXNUMP (obj))
322 return XFIXNUM (obj);
323
324 if (EQ (obj, Qstring))
325 return 'CSTR';
326 else if (EQ (obj, Qshort))
327 return 'SHRT';
328 else if (EQ (obj, Qlong))
329 return 'LONG';
330 else if (EQ (obj, Qllong))
331 return 'LLNG';
332 else if (EQ (obj, Qbyte))
333 return 'BYTE';
334 else if (EQ (obj, Qref))
335 return 'RREF';
336 else if (EQ (obj, Qchar))
337 return 'CHAR';
338 else if (EQ (obj, Qbool))
339 return 'BOOL';
340 else
341 return -1;
342}
343
344static void
345haiku_lisp_to_message (Lisp_Object obj, void *message)
346{
347 Lisp_Object tem, t1, name, type_sym, t2, data;
348 int32 type_code, long_data;
349 int16 short_data;
350 int64 llong_data;
351 int8 char_data;
352 bool bool_data;
353 intmax_t t4;
354
355 CHECK_LIST (obj);
356 for (tem = obj; CONSP (tem); tem = XCDR (tem))
357 {
358 t1 = XCAR (tem);
359 CHECK_CONS (t1);
360
361 name = XCAR (t1);
362 CHECK_STRING (name);
363
364 t1 = XCDR (t1);
365 CHECK_CONS (t1);
366
367 type_sym = XCAR (t1);
368 type_code = lisp_to_type_code (type_sym);
369
370 if (type_code == -1)
371 signal_error ("Unknown data type", type_sym);
372
373 CHECK_LIST (t1);
374 for (t2 = XCDR (t1); CONSP (t2); t2 = XCDR (t2))
375 {
376 data = XCAR (t2);
377
378 switch (type_code)
379 {
380 case 'RREF':
381 signal_error ("Cannot deserialize data type", type_sym);
382 break;
383
384 case 'SHRT':
385 if (!TYPE_RANGED_FIXNUMP (int16, data))
386 signal_error ("Invalid value", data);
387 short_data = XFIXNUM (data);
388
389 block_input ();
390 be_add_message_data (message, SSDATA (name),
391 type_code, &short_data,
392 sizeof short_data);
393 unblock_input ();
394 break;
395
396 case 'LONG':
397 if (BIGNUMP (data))
398 {
399 t4 = bignum_to_intmax (data);
400
401 /* We know that int32 is signed. */
402 if (!t4 || t4 > TYPE_MINIMUM (int32)
403 || t4 < TYPE_MAXIMUM (int32))
404 signal_error ("Value too large", data);
405
406 long_data = (int32) t4;
407 }
408 else
409 {
410 if (!TYPE_RANGED_FIXNUMP (int32, data))
411 signal_error ("Invalid value", data);
412
413 long_data = (int32) XFIXNUM (data);
414 }
415
416 block_input ();
417 be_add_message_data (message, SSDATA (name),
418 type_code, &long_data,
419 sizeof long_data);
420 unblock_input ();
421 break;
422
423 case 'LLNG':
424 if (BIGNUMP (data))
425 {
426 t4 = bignum_to_intmax (data);
427
428 if (!t4 || t4 > TYPE_MINIMUM (int64)
429 || t4 < TYPE_MAXIMUM (int64))
430 signal_error ("Value too large", data);
431
432 llong_data = (int64) t4;
433 }
434 else
435 {
436 if (!TYPE_RANGED_FIXNUMP (int64, data))
437 signal_error ("Invalid value", data);
438
439 llong_data = (int64) XFIXNUM (data);
440 }
441
442 block_input ();
443 be_add_message_data (message, SSDATA (name),
444 type_code, &llong_data,
445 sizeof llong_data);
446 unblock_input ();
447 break;
448
449 case 'CHAR':
450 case 'BYTE':
451 if (!TYPE_RANGED_FIXNUMP (int8, data))
452 signal_error ("Invalid value", data);
453 char_data = XFIXNUM (data);
454
455 block_input ();
456 be_add_message_data (message, SSDATA (name),
457 type_code, &char_data,
458 sizeof char_data);
459 unblock_input ();
460 break;
461
462 case 'BOOL':
463 bool_data = !NILP (data);
464
465 block_input ();
466 be_add_message_data (message, SSDATA (name),
467 type_code, &bool_data,
468 sizeof bool_data);
469 unblock_input ();
470 break;
471
472 default:
473 CHECK_STRING (data);
474
475 block_input ();
476 be_add_message_data (message, SSDATA (name),
477 type_code, SDATA (data),
478 SBYTES (data));
479 unblock_input ();
480 }
481 }
482 CHECK_LIST_END (t2, t1);
483 }
484 CHECK_LIST_END (tem, obj);
485}
486
487DEFUN ("haiku-drag-message", Fhaiku_drag_message, Shaiku_drag_message,
488 2, 2, 0,
489 doc: /* Begin dragging MESSAGE from FRAME.
490
491MESSAGE an alist of strings, denoting message field names, to a list
492the form (TYPE DATA ...), where TYPE is an integer denoting the system
493data type of DATA, and DATA is in the general case a unibyte string.
494
495If TYPE is a symbol instead of an integer, then DATA was specially
496decoded. If TYPE is `ref', then DATA is the absolute file name of a
497file, or nil if decoding the file name failed. If TYPE is `string',
498then DATA is a unibyte string. If TYPE is `short', then DATA is a
49916-bit signed integer. If TYPE is `long', then DATA is a 32-bit
500signed integer. If TYPE is `llong', then DATA is a 64-bit signed
501integer. If TYPE is `byte' or `char', then DATA is an 8-bit signed
502integer. If TYPE is `bool', then DATA is a boolean.
503
504FRAME is a window system frame that must be visible, from which the
505drag will originate. */)
506 (Lisp_Object frame, Lisp_Object message)
507{
508 specpdl_ref idx;
509 void *be_message;
510 struct frame *f;
511
512 idx = SPECPDL_INDEX ();
513 f = decode_window_system_frame (frame);
514
515 if (!FRAME_VISIBLE_P (f))
516 error ("Frame is invisible");
517
518 be_message = be_create_simple_message ();
519
520 record_unwind_protect_ptr (BMessage_delete, be_message);
521 haiku_lisp_to_message (message, be_message);
522 be_drag_message (FRAME_HAIKU_VIEW (f), be_message,
523 block_input, unblock_input,
524 process_pending_signals);
525
526 return unbind_to (idx, Qnil);
527}
528
314void 529void
315syms_of_haikuselect (void) 530syms_of_haikuselect (void)
316{ 531{
@@ -333,4 +548,5 @@ syms_of_haikuselect (void)
333 defsubr (&Shaiku_selection_put); 548 defsubr (&Shaiku_selection_put);
334 defsubr (&Shaiku_selection_targets); 549 defsubr (&Shaiku_selection_targets);
335 defsubr (&Shaiku_selection_owner_p); 550 defsubr (&Shaiku_selection_owner_p);
551 defsubr (&Shaiku_drag_message);
336} 552}
diff --git a/src/haikuselect.h b/src/haikuselect.h
index 5b9abc7a8aa..366890d1a46 100644
--- a/src/haikuselect.h
+++ b/src/haikuselect.h
@@ -87,6 +87,10 @@ extern "C"
87 ssize_t *size_return); 87 ssize_t *size_return);
88 extern int be_get_refs_data (void *message, const char *name, 88 extern int be_get_refs_data (void *message, const char *name,
89 int32 index, char **path_buffer); 89 int32 index, char **path_buffer);
90 extern void *be_create_simple_message (void);
91 extern int be_add_message_data (void *message, const char *name,
92 int32 type_code, const void *buf,
93 ssize_t buf_size);
90#ifdef __cplusplus 94#ifdef __cplusplus
91}; 95};
92#endif 96#endif