diff options
Diffstat (limited to 'src/haiku_select.cc')
-rw-r--r-- | src/haiku_select.cc | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/src/haiku_select.cc b/src/haiku_select.cc new file mode 100644 index 00000000000..872da1d6c44 --- /dev/null +++ b/src/haiku_select.cc @@ -0,0 +1,519 @@ +/* Haiku window system selection support. Hey Emacs, this is -*- C++ -*- + 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/>. */ + +#include <config.h> + +#include <Application.h> +#include <Clipboard.h> +#include <Message.h> +#include <Path.h> +#include <Entry.h> + +#include <cstdlib> +#include <cstring> + +#include "haikuselect.h" + +/* The clipboard object representing the primary selection. */ +static BClipboard *primary = NULL; + +/* The clipboard object representing the secondary selection. */ +static BClipboard *secondary = NULL; + +/* The clipboard object used by other programs, representing the + clipboard. */ +static BClipboard *system_clipboard = NULL; + +/* The number of times the system clipboard has changed. */ +static int64 count_clipboard = -1; + +/* The number of times the primary selection has changed. */ +static int64 count_primary = -1; + +/* The number of times the secondary selection has changed. */ +static int64 count_secondary = -1; + +/* Whether or not we currently think Emacs owns the primary + selection. */ +static bool owned_primary; + +/* Likewise for the secondary selection. */ +static bool owned_secondary; + +/* And the clipboard. */ +static bool owned_clipboard; + +static BClipboard * +get_clipboard_object (enum haiku_clipboard clipboard) +{ + switch (clipboard) + { + case CLIPBOARD_PRIMARY: + return primary; + + case CLIPBOARD_SECONDARY: + return secondary; + + case CLIPBOARD_CLIPBOARD: + return system_clipboard; + } + + abort (); +} + +static char * +be_find_clipboard_data_1 (BClipboard *cb, const char *type, ssize_t *len) +{ + BMessage *data; + const char *ptr; + ssize_t nbytes; + void *value; + + if (!cb->Lock ()) + return NULL; + + data = cb->Data (); + + if (!data) + { + cb->Unlock (); + return NULL; + } + + data->FindData (type, B_MIME_TYPE, (const void **) &ptr, + &nbytes); + + if (!ptr) + { + cb->Unlock (); + return NULL; + } + + if (len) + *len = nbytes; + + value = malloc (nbytes); + + if (!data) + { + cb->Unlock (); + return NULL; + } + + memcpy (value, ptr, nbytes); + cb->Unlock (); + + return (char *) value; +} + +static void +be_set_clipboard_data_1 (BClipboard *cb, const char *type, const char *data, + ssize_t len, bool clear) +{ + BMessage *message_data; + + if (!cb->Lock ()) + return; + + if (clear) + cb->Clear (); + + message_data = cb->Data (); + + if (!message_data) + { + cb->Unlock (); + return; + } + + if (data) + { + if (message_data->ReplaceData (type, B_MIME_TYPE, data, len) + == B_NAME_NOT_FOUND) + message_data->AddData (type, B_MIME_TYPE, data, len); + } + else + message_data->RemoveName (type); + + cb->Commit (); + cb->Unlock (); +} + +void +be_update_clipboard_count (enum haiku_clipboard id) +{ + switch (id) + { + case CLIPBOARD_CLIPBOARD: + count_clipboard = system_clipboard->SystemCount (); + owned_clipboard = true; + break; + + case CLIPBOARD_PRIMARY: + count_primary = primary->SystemCount (); + owned_primary = true; + break; + + case CLIPBOARD_SECONDARY: + count_secondary = secondary->SystemCount (); + owned_secondary = true; + break; + } +} + +char * +be_find_clipboard_data (enum haiku_clipboard id, const char *type, + ssize_t *len) +{ + return be_find_clipboard_data_1 (get_clipboard_object (id), + type, len); +} + +void +be_set_clipboard_data (enum haiku_clipboard id, const char *type, + const char *data, ssize_t len, bool clear) +{ + be_update_clipboard_count (id); + + be_set_clipboard_data_1 (get_clipboard_object (id), type, + data, len, clear); +} + +static bool +clipboard_owner_p (void) +{ + return (count_clipboard >= 0 + && (count_clipboard + 1 + == system_clipboard->SystemCount ())); +} + +static bool +primary_owner_p (void) +{ + return (count_primary >= 0 + && (count_primary + 1 + == primary->SystemCount ())); +} + +static bool +secondary_owner_p (void) +{ + return (count_secondary >= 0 + && (count_secondary + 1 + == secondary->SystemCount ())); +} + +bool +be_clipboard_owner_p (enum haiku_clipboard clipboard) +{ + switch (clipboard) + { + case CLIPBOARD_PRIMARY: + return primary_owner_p (); + + case CLIPBOARD_SECONDARY: + return secondary_owner_p (); + + case CLIPBOARD_CLIPBOARD: + return clipboard_owner_p (); + } + + abort (); +} + +void +be_clipboard_init (void) +{ + system_clipboard = new BClipboard ("system"); + primary = new BClipboard ("primary"); + secondary = new BClipboard ("secondary"); +} + +int +be_enum_message (void *message, int32 *tc, int32 index, + int32 *count, const char **name_return) +{ + BMessage *msg = (BMessage *) message; + type_code type; + char *name; + status_t rc; + + rc = msg->GetInfo (B_ANY_TYPE, index, &name, &type, count); + + if (rc != B_OK) + return 1; + + *tc = type; + *name_return = name; + return 0; +} + +int +be_get_refs_data (void *message, const char *name, + int32 index, char **path_buffer) +{ + status_t rc; + BEntry entry; + BPath path; + entry_ref ref; + BMessage *msg; + + msg = (BMessage *) message; + rc = msg->FindRef (name, index, &ref); + + if (rc != B_OK) + return 1; + + rc = entry.SetTo (&ref, 0); + + if (rc != B_OK) + return 1; + + rc = entry.GetPath (&path); + + if (rc != B_OK) + return 1; + + *path_buffer = strdup (path.Path ()); + return 0; +} + +int +be_get_point_data (void *message, const char *name, + int32 index, float *x, float *y) +{ + status_t rc; + BMessage *msg; + BPoint point; + + msg = (BMessage *) message; + rc = msg->FindPoint (name, index, &point); + + if (rc != B_OK) + return 1; + + *x = point.x; + *y = point.y; + + return 0; +} + +int +be_get_message_data (void *message, const char *name, + int32 type_code, int32 index, + const void **buf_return, + ssize_t *size_return) +{ + BMessage *msg = (BMessage *) message; + + return msg->FindData (name, type_code, + index, buf_return, size_return) != B_OK; +} + +uint32 +be_get_message_type (void *message) +{ + BMessage *msg = (BMessage *) message; + + return msg->what; +} + +void +be_set_message_type (void *message, uint32 what) +{ + BMessage *msg = (BMessage *) message; + + msg->what = what; +} + +void * +be_get_message_message (void *message, const char *name, + int32 index) +{ + BMessage *msg = (BMessage *) message; + BMessage *out = new (std::nothrow) BMessage; + + if (!out) + return NULL; + + if (msg->FindMessage (name, index, out) != B_OK) + { + delete out; + return NULL; + } + + return out; +} + +void * +be_create_simple_message (void) +{ + return new BMessage (B_SIMPLE_DATA); +} + +int +be_add_message_data (void *message, const char *name, + int32 type_code, const void *buf, + ssize_t buf_size) +{ + BMessage *msg = (BMessage *) message; + + return msg->AddData (name, type_code, buf, buf_size) != B_OK; +} + +int +be_add_refs_data (void *message, const char *name, + const char *filename) +{ + BEntry entry (filename); + entry_ref ref; + BMessage *msg = (BMessage *) message; + + if (entry.InitCheck () != B_OK) + return 1; + + if (entry.GetRef (&ref) != B_OK) + return 1; + + return msg->AddRef (name, &ref) != B_OK; +} + +int +be_add_point_data (void *message, const char *name, + float x, float y) +{ + BMessage *msg = (BMessage *) message; + + return msg->AddPoint (name, BPoint (x, y)) != B_OK; +} + +int +be_add_message_message (void *message, const char *name, + void *data) +{ + BMessage *msg = (BMessage *) message; + BMessage *data_message = (BMessage *) data; + + if (msg->AddMessage (name, data_message) != B_OK) + return 1; + + return 0; +} + +int +be_lock_clipboard_message (enum haiku_clipboard clipboard, + void **message_return, bool clear) +{ + BClipboard *board; + + board = get_clipboard_object (clipboard); + + if (!board->Lock ()) + return 1; + + if (clear) + board->Clear (); + + *message_return = board->Data (); + return 0; +} + +void +be_unlock_clipboard (enum haiku_clipboard clipboard, bool discard) +{ + BClipboard *board; + + board = get_clipboard_object (clipboard); + + if (discard) + board->Revert (); + else + board->Commit (); + + board->Unlock (); +} + +void +be_handle_clipboard_changed_message (void) +{ + int64 n_clipboard, n_primary, n_secondary; + + n_clipboard = system_clipboard->SystemCount (); + n_primary = primary->SystemCount (); + n_secondary = secondary->SystemCount (); + + if (count_clipboard != -1 + && (n_clipboard > count_clipboard + 1) + && owned_clipboard) + { + owned_clipboard = false; + haiku_selection_disowned (CLIPBOARD_CLIPBOARD, + n_clipboard); + } + + if (count_primary != -1 + && (n_primary > count_primary + 1) + && owned_primary) + { + owned_primary = false; + haiku_selection_disowned (CLIPBOARD_PRIMARY, + n_primary); + } + + if (count_secondary != -1 + && (n_secondary > count_secondary + 1) + && owned_secondary) + { + owned_secondary = false; + haiku_selection_disowned (CLIPBOARD_SECONDARY, + n_secondary); + } +} + +void +be_start_watching_selection (enum haiku_clipboard id) +{ + BClipboard *clipboard; + + clipboard = get_clipboard_object (id); + clipboard->StartWatching (be_app); +} + +bool +be_selection_outdated_p (enum haiku_clipboard id, int64 count) +{ + if (id == CLIPBOARD_CLIPBOARD && count_clipboard > count) + return true; + + if (id == CLIPBOARD_PRIMARY && count_primary > count) + return true; + + if (id == CLIPBOARD_SECONDARY && count_secondary > count) + return true; + + return false; +} + +int64 +be_get_clipboard_count (enum haiku_clipboard id) +{ + BClipboard *clipboard; + + clipboard = get_clipboard_object (id); + return clipboard->SystemCount (); +} |