[View|Xcb] Add support to copy current selected item to clipboard

Adds control-v binding that copies it to the clipboard.
THIS ONLY WORKS WITH CLIPBOARD MANAGER!!! once rofi is closes, the data is
gone!

This needs to be tested. Documentation on this is lacking so reversed
engineered from other applications.

TODO: how do we do the SAVE_TARGETS?

fixes: #378
This commit is contained in:
Dave Davenport 2022-08-22 21:44:52 +02:00
parent d1e275314e
commit fc07619ac6
8 changed files with 102 additions and 13 deletions

View File

@ -96,6 +96,13 @@ Paste clipboard
.PP
\fBDefault\fP: Control+v,Insert
.SS \fBkb-secondary-copy\fP
.PP
Copy current selection to clipboard
.PP
\fBDefault\fP: Control+c
.SS \fBkb-clear-line\fP
.PP
Clear input line

View File

@ -65,6 +65,12 @@ Paste clipboard
**Default**: Control+v,Insert
### **kb-secondary-copy**
Copy current selection to clipboard
**Default**: Control+c
### **kb-clear-line**
Clear input line

View File

@ -60,6 +60,8 @@ typedef enum {
PASTE_PRIMARY = 1,
/** Paste from secondary clipboard */
PASTE_SECONDARY,
/** Copy to secondary clipboard */
COPY_SECONDARY,
/** Clear the entry box. */
CLEAR_LINE,
/** Move to front of text */

View File

@ -61,6 +61,7 @@ struct _xcb_stuff {
NkBindingsSeat *bindings_seat;
gboolean mouse_seen;
xcb_window_t focus_revert;
char *clipboard;
};
#endif

View File

@ -41,6 +41,8 @@ typedef struct _xcb_stuff xcb_stuff;
*/
extern xcb_stuff *xcb;
void xcb_stuff_set_clipboard(char *data);
/**
* Get the root window.
*
@ -77,9 +79,9 @@ void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms,
/** Atoms we want to pre-load */
#define EWMH_ATOMS(X) \
X(_NET_WM_WINDOW_OPACITY), X(I3_SOCKET_PATH), X(UTF8_STRING), X(STRING), \
X(CLIPBOARD), X(WM_WINDOW_ROLE), X(_XROOTPMAP_ID), X(_MOTIF_WM_HINTS), \
X(WM_TAKE_FOCUS), X(ESETROOT_PMAP_ID)
X(_NET_WM_WINDOW_OPACITY), X(I3_SOCKET_PATH), X(TARGETS), X(UTF8_STRING), \
X(STRING), X(CLIPBOARD), X(WM_WINDOW_ROLE), X(_XROOTPMAP_ID), \
X(_MOTIF_WM_HINTS), X(WM_TAKE_FOCUS), X(ESETROOT_PMAP_ID)
/** enumeration of the atoms. */
enum { EWMH_ATOMS(ATOM_ENUM), NUM_NETATOMS };

View File

@ -24,12 +24,11 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <glib.h>
#include <string.h>
#include "nkutils-bindings.h"
#include "rofi.h"
#include "xrmoptions.h"
#include <string.h>
typedef struct {
guint id;
@ -51,6 +50,10 @@ ActionBindingEntry rofi_bindings[] = {
.name = "kb-secondary-paste",
.binding = "Control+v,Insert",
.comment = "Paste clipboard"},
{.id = COPY_SECONDARY,
.name = "kb-secondary-copy",
.binding = "Control+c",
.comment = "Copy to clipboard"},
{.id = CLEAR_LINE,
.name = "kb-clear-line",
.binding = "Control+w",

View File

@ -1374,6 +1374,21 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME);
xcb_flush(xcb->connection);
break;
case COPY_SECONDARY: {
char *data = NULL;
unsigned int selected = listview_get_selected(state->list_view);
if (selected < state->filtered_lines) {
data = mode_get_completion(state->sw, state->line_map[selected]);
} else if (state->text && state->text->text) {
data = g_strdup(state->text->text);
}
if (data) {
xcb_stuff_set_clipboard(data);
xcb_set_selection_owner(xcb->connection, CacheState.main_window,
netatoms[CLIPBOARD], XCB_CURRENT_TIME);
xcb_flush(xcb->connection);
}
} break;
case SCREENSHOT:
rofi_capture_screenshot();
break;
@ -2265,6 +2280,8 @@ void rofi_view_hide(void) {
}
void rofi_view_cleanup() {
// Clear clipboard data.
xcb_stuff_set_clipboard(NULL);
g_debug("Cleanup.");
if (CacheState.idle_timeout > 0) {
g_source_remove(CacheState.idle_timeout);

View File

@ -87,7 +87,8 @@ struct _xcb_stuff xcb_int = {.connection = NULL,
.screen_nbr = -1,
.sndisplay = NULL,
.sncontext = NULL,
.monitors = NULL};
.monitors = NULL,
.clipboard = NULL};
xcb_stuff *xcb = &xcb_int;
/**
@ -1216,6 +1217,52 @@ static void main_loop_x11_event_handler_view(xcb_generic_event_t *event) {
}
break;
}
case XCB_SELECTION_CLEAR: {
g_debug("Selection Clear.");
xcb_stuff_set_clipboard(NULL);
} break;
case XCB_SELECTION_REQUEST: {
g_debug("Selection Request.");
xcb_selection_request_event_t *req = (xcb_selection_request_event_t *)event;
if (req->selection == netatoms[CLIPBOARD]) {
xcb_atom_t targets[2];
xcb_selection_notify_event_t selection_notify = {
.response_type = XCB_SELECTION_NOTIFY,
.sequence = 0,
.time = req->time,
.requestor = req->requestor,
.selection = req->selection,
.target = req->target,
.property = XCB_ATOM_NONE,
};
// If no clipboard, we return NONE.
if (xcb->clipboard) {
// Request for UTF-8
if (req->target == netatoms[UTF8_STRING]) {
g_debug("Selection Request UTF-8.");
xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
req->requestor, req->property,
netatoms[UTF8_STRING], 8,
strlen(xcb->clipboard) + 1, xcb->clipboard);
selection_notify.property = req->property;
} else if (req->target == netatoms[TARGETS]) {
g_debug("Selection Request Targets.");
// We currently only support UTF8 from clipboard. So indicate this.
targets[0] = netatoms[UTF8_STRING];
xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE,
req->requestor, req->property, XCB_ATOM_ATOM, 32,
1, targets);
selection_notify.property = req->property;
}
}
xcb_send_event(xcb->connection,
0, // propagate
req->requestor, XCB_EVENT_MASK_NO_EVENT,
(const char *)&selection_notify);
xcb_flush(xcb->connection);
}
} break;
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *bre = (xcb_button_release_event_t *)event;
NkBindingsMouseButton button;
@ -1257,14 +1304,13 @@ static void main_loop_x11_event_handler_view(xcb_generic_event_t *event) {
gchar *text;
xcb->last_timestamp = xkpe->time;
if ( config.xserver_i300_workaround ) {
if (config.xserver_i300_workaround) {
text = nk_bindings_seat_handle_key_with_modmask(
xcb->bindings_seat, NULL, xkpe->state, xkpe->detail,
NK_BINDINGS_KEY_STATE_PRESS);
} else {
text = nk_bindings_seat_handle_key(
xcb->bindings_seat, NULL, xkpe->detail,
NK_BINDINGS_KEY_STATE_PRESS);
text = nk_bindings_seat_handle_key(xcb->bindings_seat, NULL, xkpe->detail,
NK_BINDINGS_KEY_STATE_PRESS);
}
if (text != NULL) {
rofi_view_handle_text(state, text);
@ -1294,9 +1340,10 @@ static gboolean main_loop_x11_event_handler(xcb_generic_event_t *ev,
g_main_loop_quit(xcb->main_loop);
return G_SOURCE_REMOVE;
}
// DD: it seems this handler often gets dispatched while the queue in GWater is empty.
// resulting in a NULL for ev. This seems not an error.
//g_warning("main_loop_x11_event_handler: ev == NULL, status == %d", status);
// DD: it seems this handler often gets dispatched while the queue in GWater
// is empty. resulting in a NULL for ev. This seems not an error.
// g_warning("main_loop_x11_event_handler: ev == NULL, status == %d",
// status);
return G_SOURCE_CONTINUE;
}
uint8_t type = ev->response_type & ~0x80;
@ -1815,3 +1862,7 @@ void x11_set_cursor(xcb_window_t window, X11CursorType type) {
xcb_change_window_attributes(xcb->connection, window, XCB_CW_CURSOR,
&(cursors[type]));
}
void xcb_stuff_set_clipboard(char *data) {
g_free(xcb->clipboard);
xcb->clipboard = data;
}