root/src/haiku_select.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_clipboard_object
  2. be_find_clipboard_data_1
  3. be_set_clipboard_data_1
  4. be_update_clipboard_count
  5. be_find_clipboard_data
  6. be_set_clipboard_data
  7. clipboard_owner_p
  8. primary_owner_p
  9. secondary_owner_p
  10. be_clipboard_owner_p
  11. be_clipboard_init
  12. be_enum_message
  13. be_get_refs_data
  14. be_get_point_data
  15. be_get_message_data
  16. be_get_message_type
  17. be_set_message_type
  18. be_get_message_message
  19. be_create_simple_message
  20. be_add_message_data
  21. be_add_refs_data
  22. be_add_point_data
  23. be_add_message_message
  24. be_lock_clipboard_message
  25. be_unlock_clipboard
  26. be_handle_clipboard_changed_message
  27. be_start_watching_selection
  28. be_selection_outdated_p
  29. be_get_clipboard_count
  30. type_for_type
  31. my_team_id
  32. be_display_notification

     1 /* Haiku window system selection support. Hey Emacs, this is -*- C++ -*-
     2    Copyright (C) 2021-2023 Free Software Foundation, Inc.
     3 
     4 This file is part of GNU Emacs.
     5 
     6 GNU Emacs is free software: you can redistribute it and/or modify
     7 it under the terms of the GNU General Public License as published by
     8 the Free Software Foundation, either version 3 of the License, or (at
     9 your option) any later version.
    10 
    11 GNU Emacs is distributed in the hope that it will be useful,
    12 but WITHOUT ANY WARRANTY; without even the implied warranty of
    13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14 GNU General Public License for more details.
    15 
    16 You should have received a copy of the GNU General Public License
    17 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    18 
    19 #include <config.h>
    20 #include <intprops.h>
    21 
    22 #include <Application.h>
    23 #include <Bitmap.h>
    24 #include <Clipboard.h>
    25 #include <Entry.h>
    26 #include <Message.h>
    27 #include <Notification.h>
    28 #include <OS.h>
    29 #include <Path.h>
    30 #include <String.h>
    31 
    32 #include <translation/TranslationUtils.h>
    33 
    34 #include <cstdlib>
    35 #include <cstring>
    36 #include <cstdint>
    37 #include <cstdio>
    38 
    39 #include "haikuselect.h"
    40 
    41 /* The clipboard object representing the primary selection.  */
    42 static BClipboard *primary = NULL;
    43 
    44 /* The clipboard object representing the secondary selection.  */
    45 static BClipboard *secondary = NULL;
    46 
    47 /* The clipboard object used by other programs, representing the
    48    clipboard.  */
    49 static BClipboard *system_clipboard = NULL;
    50 
    51 /* The number of times the system clipboard has changed.  */
    52 static int64 count_clipboard = -1;
    53 
    54 /* The number of times the primary selection has changed.  */
    55 static int64 count_primary = -1;
    56 
    57 /* The number of times the secondary selection has changed.  */
    58 static int64 count_secondary = -1;
    59 
    60 /* Whether or not we currently think Emacs owns the primary
    61    selection.  */
    62 static bool owned_primary;
    63 
    64 /* Likewise for the secondary selection.  */
    65 static bool owned_secondary;
    66 
    67 /* And the clipboard.  */
    68 static bool owned_clipboard;
    69 
    70 
    71 
    72 /* C++ clipboard support.  */
    73 
    74 static BClipboard *
    75 get_clipboard_object (enum haiku_clipboard clipboard)
    76 {
    77   switch (clipboard)
    78     {
    79     case CLIPBOARD_PRIMARY:
    80       return primary;
    81 
    82     case CLIPBOARD_SECONDARY:
    83       return secondary;
    84 
    85     case CLIPBOARD_CLIPBOARD:
    86       return system_clipboard;
    87     }
    88 
    89   abort ();
    90 }
    91 
    92 static char *
    93 be_find_clipboard_data_1 (BClipboard *cb, const char *type, ssize_t *len)
    94 {
    95   BMessage *data;
    96   const char *ptr;
    97   ssize_t nbytes;
    98   void *value;
    99 
   100   if (!cb->Lock ())
   101     return NULL;
   102 
   103   data = cb->Data ();
   104 
   105   if (!data)
   106     {
   107       cb->Unlock ();
   108       return NULL;
   109     }
   110 
   111   data->FindData (type, B_MIME_TYPE, (const void **) &ptr,
   112                   &nbytes);
   113 
   114   if (!ptr)
   115     {
   116       cb->Unlock ();
   117       return NULL;
   118     }
   119 
   120   if (len)
   121     *len = nbytes;
   122 
   123   value = malloc (nbytes);
   124 
   125   if (!data)
   126     {
   127       cb->Unlock ();
   128       return NULL;
   129     }
   130 
   131   memcpy (value, ptr, nbytes);
   132   cb->Unlock ();
   133 
   134   return (char *) value;
   135 }
   136 
   137 static void
   138 be_set_clipboard_data_1 (BClipboard *cb, const char *type, const char *data,
   139                          ssize_t len, bool clear)
   140 {
   141   BMessage *message_data;
   142 
   143   if (!cb->Lock ())
   144     return;
   145 
   146   if (clear)
   147     cb->Clear ();
   148 
   149   message_data = cb->Data ();
   150 
   151   if (!message_data)
   152     {
   153       cb->Unlock ();
   154       return;
   155     }
   156 
   157   if (data)
   158     {
   159       if (message_data->ReplaceData (type, B_MIME_TYPE, data, len)
   160           == B_NAME_NOT_FOUND)
   161         message_data->AddData (type, B_MIME_TYPE, data, len);
   162     }
   163   else
   164     message_data->RemoveName (type);
   165 
   166   cb->Commit ();
   167   cb->Unlock ();
   168 }
   169 
   170 void
   171 be_update_clipboard_count (enum haiku_clipboard id)
   172 {
   173   switch (id)
   174     {
   175     case CLIPBOARD_CLIPBOARD:
   176       count_clipboard = system_clipboard->SystemCount ();
   177       owned_clipboard = true;
   178       break;
   179 
   180     case CLIPBOARD_PRIMARY:
   181       count_primary = primary->SystemCount ();
   182       owned_primary = true;
   183       break;
   184 
   185     case CLIPBOARD_SECONDARY:
   186       count_secondary = secondary->SystemCount ();
   187       owned_secondary = true;
   188       break;
   189     }
   190 }
   191 
   192 char *
   193 be_find_clipboard_data (enum haiku_clipboard id, const char *type,
   194                         ssize_t *len)
   195 {
   196   return be_find_clipboard_data_1 (get_clipboard_object (id),
   197                                    type, len);
   198 }
   199 
   200 void
   201 be_set_clipboard_data (enum haiku_clipboard id, const char *type,
   202                        const char *data, ssize_t len, bool clear)
   203 {
   204   be_update_clipboard_count (id);
   205 
   206   be_set_clipboard_data_1 (get_clipboard_object (id), type,
   207                            data, len, clear);
   208 }
   209 
   210 static bool
   211 clipboard_owner_p (void)
   212 {
   213   return (count_clipboard >= 0
   214           && (count_clipboard + 1
   215               == system_clipboard->SystemCount ()));
   216 }
   217 
   218 static bool
   219 primary_owner_p (void)
   220 {
   221   return (count_primary >= 0
   222           && (count_primary + 1
   223               == primary->SystemCount ()));
   224 }
   225 
   226 static bool
   227 secondary_owner_p (void)
   228 {
   229   return (count_secondary >= 0
   230           && (count_secondary + 1
   231               == secondary->SystemCount ()));
   232 }
   233 
   234 bool
   235 be_clipboard_owner_p (enum haiku_clipboard clipboard)
   236 {
   237   switch (clipboard)
   238     {
   239     case CLIPBOARD_PRIMARY:
   240       return primary_owner_p ();
   241 
   242     case CLIPBOARD_SECONDARY:
   243       return secondary_owner_p ();
   244 
   245     case CLIPBOARD_CLIPBOARD:
   246       return clipboard_owner_p ();
   247     }
   248 
   249   abort ();
   250 }
   251 
   252 void
   253 be_clipboard_init (void)
   254 {
   255   system_clipboard = new BClipboard ("system");
   256   primary = new BClipboard ("primary");
   257   secondary = new BClipboard ("secondary");
   258 }
   259 
   260 int
   261 be_enum_message (void *message, int32 *tc, int32 index,
   262                  int32 *count, const char **name_return)
   263 {
   264   BMessage *msg = (BMessage *) message;
   265   type_code type;
   266   char *name;
   267   status_t rc;
   268 
   269   rc = msg->GetInfo (B_ANY_TYPE, index, &name, &type, count);
   270 
   271   if (rc != B_OK)
   272     return 1;
   273 
   274   *tc = type;
   275   *name_return = name;
   276   return 0;
   277 }
   278 
   279 int
   280 be_get_refs_data (void *message, const char *name,
   281                   int32 index, char **path_buffer)
   282 {
   283   status_t rc;
   284   BEntry entry;
   285   BPath path;
   286   entry_ref ref;
   287   BMessage *msg;
   288 
   289   msg = (BMessage *) message;
   290   rc = msg->FindRef (name, index, &ref);
   291 
   292   if (rc != B_OK)
   293     return 1;
   294 
   295   rc = entry.SetTo (&ref, 0);
   296 
   297   if (rc != B_OK)
   298     return 1;
   299 
   300   rc = entry.GetPath (&path);
   301 
   302   if (rc != B_OK)
   303     return 1;
   304 
   305   *path_buffer = strdup (path.Path ());
   306   return 0;
   307 }
   308 
   309 int
   310 be_get_point_data (void *message, const char *name,
   311                    int32 index, float *x, float *y)
   312 {
   313   status_t rc;
   314   BMessage *msg;
   315   BPoint point;
   316 
   317   msg = (BMessage *) message;
   318   rc = msg->FindPoint (name, index, &point);
   319 
   320   if (rc != B_OK)
   321     return 1;
   322 
   323   *x = point.x;
   324   *y = point.y;
   325 
   326   return 0;
   327 }
   328 
   329 int
   330 be_get_message_data (void *message, const char *name,
   331                      int32 type_code, int32 index,
   332                      const void **buf_return,
   333                      ssize_t *size_return)
   334 {
   335   BMessage *msg = (BMessage *) message;
   336 
   337   return msg->FindData (name, type_code,
   338                         index, buf_return, size_return) != B_OK;
   339 }
   340 
   341 uint32
   342 be_get_message_type (void *message)
   343 {
   344   BMessage *msg = (BMessage *) message;
   345 
   346   return msg->what;
   347 }
   348 
   349 void
   350 be_set_message_type (void *message, uint32 what)
   351 {
   352   BMessage *msg = (BMessage *) message;
   353 
   354   msg->what = what;
   355 }
   356 
   357 void *
   358 be_get_message_message (void *message, const char *name,
   359                         int32 index)
   360 {
   361   BMessage *msg = (BMessage *) message;
   362   BMessage *out = new (std::nothrow) BMessage;
   363 
   364   if (!out)
   365     return NULL;
   366 
   367   if (msg->FindMessage (name, index, out) != B_OK)
   368     {
   369       delete out;
   370       return NULL;
   371     }
   372 
   373   return out;
   374 }
   375 
   376 void *
   377 be_create_simple_message (void)
   378 {
   379   return new BMessage (B_SIMPLE_DATA);
   380 }
   381 
   382 int
   383 be_add_message_data (void *message, const char *name,
   384                      int32 type_code, const void *buf,
   385                      ssize_t buf_size)
   386 {
   387   BMessage *msg = (BMessage *) message;
   388 
   389   return msg->AddData (name, type_code, buf, buf_size) != B_OK;
   390 }
   391 
   392 int
   393 be_add_refs_data (void *message, const char *name,
   394                   const char *filename)
   395 {
   396   BEntry entry (filename);
   397   entry_ref ref;
   398   BMessage *msg = (BMessage *) message;
   399 
   400   if (entry.InitCheck () != B_OK)
   401     return 1;
   402 
   403   if (entry.GetRef (&ref) != B_OK)
   404     return 1;
   405 
   406   return msg->AddRef (name, &ref) != B_OK;
   407 }
   408 
   409 int
   410 be_add_point_data (void *message, const char *name,
   411                    float x, float y)
   412 {
   413   BMessage *msg = (BMessage *) message;
   414 
   415   return msg->AddPoint (name, BPoint (x, y)) != B_OK;
   416 }
   417 
   418 int
   419 be_add_message_message (void *message, const char *name,
   420                         void *data)
   421 {
   422   BMessage *msg = (BMessage *) message;
   423   BMessage *data_message = (BMessage *) data;
   424 
   425   if (msg->AddMessage (name, data_message) != B_OK)
   426     return 1;
   427 
   428   return 0;
   429 }
   430 
   431 int
   432 be_lock_clipboard_message (enum haiku_clipboard clipboard,
   433                            void **message_return, bool clear)
   434 {
   435   BClipboard *board;
   436 
   437   board = get_clipboard_object (clipboard);
   438 
   439   if (!board->Lock ())
   440     return 1;
   441 
   442   if (clear)
   443     board->Clear ();
   444 
   445   *message_return = board->Data ();
   446   return 0;
   447 }
   448 
   449 void
   450 be_unlock_clipboard (enum haiku_clipboard clipboard, bool discard)
   451 {
   452   BClipboard *board;
   453 
   454   board = get_clipboard_object (clipboard);
   455 
   456   if (discard)
   457     board->Revert ();
   458   else
   459     board->Commit ();
   460 
   461   board->Unlock ();
   462 }
   463 
   464 void
   465 be_handle_clipboard_changed_message (void)
   466 {
   467   int64 n_clipboard, n_primary, n_secondary;
   468 
   469   n_clipboard = system_clipboard->SystemCount ();
   470   n_primary = primary->SystemCount ();
   471   n_secondary = secondary->SystemCount ();
   472 
   473   if (count_clipboard != -1
   474       && (n_clipboard > count_clipboard + 1)
   475       && owned_clipboard)
   476     {
   477       owned_clipboard = false;
   478       haiku_selection_disowned (CLIPBOARD_CLIPBOARD,
   479                                 n_clipboard);
   480     }
   481 
   482   if (count_primary != -1
   483       && (n_primary > count_primary + 1)
   484       && owned_primary)
   485     {
   486       owned_primary = false;
   487       haiku_selection_disowned (CLIPBOARD_PRIMARY,
   488                                 n_primary);
   489     }
   490 
   491   if (count_secondary != -1
   492       && (n_secondary > count_secondary + 1)
   493       && owned_secondary)
   494     {
   495       owned_secondary = false;
   496       haiku_selection_disowned (CLIPBOARD_SECONDARY,
   497                                 n_secondary);
   498     }
   499 }
   500 
   501 void
   502 be_start_watching_selection (enum haiku_clipboard id)
   503 {
   504   BClipboard *clipboard;
   505 
   506   clipboard = get_clipboard_object (id);
   507   clipboard->StartWatching (be_app);
   508 }
   509 
   510 bool
   511 be_selection_outdated_p (enum haiku_clipboard id, int64 count)
   512 {
   513   if (id == CLIPBOARD_CLIPBOARD && count_clipboard > count)
   514     return true;
   515 
   516   if (id == CLIPBOARD_PRIMARY && count_primary > count)
   517     return true;
   518 
   519   if (id == CLIPBOARD_SECONDARY && count_secondary > count)
   520     return true;
   521 
   522   return false;
   523 }
   524 
   525 int64
   526 be_get_clipboard_count (enum haiku_clipboard id)
   527 {
   528   BClipboard *clipboard;
   529 
   530   clipboard = get_clipboard_object (id);
   531   return clipboard->SystemCount ();
   532 }
   533 
   534 
   535 
   536 /* C++ notifications support.
   537 
   538    Desktop notifications on Haiku lack some of the features furnished
   539    by notifications.el, specifically displaying multiple titled
   540    actions within a single notification, sending callbacks when the
   541    notification is dismissed, and providing a timeout after which the
   542    notification is hidden.
   543 
   544    Other features, such as notification categories and identifiers,
   545    have clean straightforward relationships with their counterparts in
   546    notifications.el.  */
   547 
   548 /* The last notification ID allocated.  */
   549 static intmax_t last_notification_id;
   550 
   551 /* Return the `enum notification_type' for TYPE.  TYPE is the TYPE
   552    argument to a call to `be_display_notification'.  */
   553 
   554 static enum notification_type
   555 type_for_type (int type)
   556 {
   557   switch (type)
   558     {
   559     case 0:
   560       return B_INFORMATION_NOTIFICATION;
   561 
   562     case 1:
   563       return B_IMPORTANT_NOTIFICATION;
   564 
   565     case 2:
   566       return B_ERROR_NOTIFICATION;
   567     }
   568 
   569   abort ();
   570 }
   571 
   572 /* Return the ID of this team.  */
   573 
   574 static team_id
   575 my_team_id (void)
   576 {
   577   thread_id id;
   578   thread_info info;
   579 
   580   id = find_thread (NULL);
   581   get_thread_info (id, &info);
   582 
   583   return info.team;
   584 }
   585 
   586 /* Display a desktop notification and return its identifier.
   587 
   588    TITLE is the title text of the notification, encoded as UTF-8 text.
   589 
   590    BODY is the text to be displayed within the body of the
   591    notification.
   592 
   593    SUPERSEDES is the identifier of a previous notification to replace,
   594    or -1 if a new notification should be displayed.
   595 
   596    TYPE states the urgency of the notification.  If 0, the
   597    notification is displayed without special decoration.  If 1, the
   598    notification is displayed with a blue band to its left, identifying
   599    it as a notification of medium importance.  If 2, the notification
   600    is displayed with a red band to its left, marking it as one of
   601    critical importance.
   602 
   603    ICON is the name of a file containing the icon of the notification,
   604    or NULL, in which case Emacs's app icon will be displayed.  */
   605 
   606 intmax_t
   607 be_display_notification (const char *title, const char *body,
   608                          intmax_t supersedes, int type, const char *icon)
   609 {
   610   intmax_t id;
   611   BNotification notification (type_for_type (type));
   612   char buffer[INT_STRLEN_BOUND (team_id)
   613               + INT_STRLEN_BOUND (intmax_t)
   614               + sizeof "."];
   615   BBitmap *bitmap;
   616 
   617   if (supersedes < 0)
   618     {
   619       /* SUPERSEDES hasn't been provided, so allocate a new
   620          notification ID.  */
   621 
   622       INT_ADD_WRAPV (last_notification_id, 1,
   623                      &last_notification_id);
   624       id = last_notification_id;
   625     }
   626   else
   627     id = supersedes;
   628 
   629   /* Set the title and body text.  */
   630   notification.SetTitle (title);
   631   notification.SetContent (body);
   632 
   633   /* Derive the notification ID from the ID of this team, so as to
   634      avoid abrogating notifications from other Emacs sessions.  */
   635   sprintf (buffer, "%d.%jd", my_team_id (), id);
   636   notification.SetMessageID (BString (buffer));
   637 
   638   /* Now set the bitmap icon, if given.  */
   639 
   640   if (icon)
   641     {
   642       bitmap = BTranslationUtils::GetBitmap (icon);
   643 
   644       if (bitmap)
   645         {
   646           notification.SetIcon (bitmap);
   647           delete bitmap;
   648         }
   649     }
   650 
   651   /* After this, Emacs::ArgvReceived should be called when the
   652      notification is clicked.  Lamentably, this does not come about,
   653      probably because arguments are only passed to applications if
   654      they are not yet running.  */
   655 #if 0
   656   notification.SetOnClickApp ("application/x-vnd.GNU-emacs");
   657   notification.AddOnClickArg (BString ("-Notification,") += buffer);
   658 #endif /* 0 */
   659 
   660   /* Finally, send the notification.  */
   661   notification.Send ();
   662   return id;
   663 }

/* [<][>][^][v][top][bottom][index][help] */