mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-25 13:55:34 -05:00
[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:
parent
d1e275314e
commit
fc07619ac6
8 changed files with 102 additions and 13 deletions
|
@ -96,6 +96,13 @@ Paste clipboard
|
||||||
.PP
|
.PP
|
||||||
\fBDefault\fP: Control+v,Insert
|
\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
|
.SS \fBkb-clear-line\fP
|
||||||
.PP
|
.PP
|
||||||
Clear input line
|
Clear input line
|
||||||
|
|
|
@ -65,6 +65,12 @@ Paste clipboard
|
||||||
|
|
||||||
**Default**: Control+v,Insert
|
**Default**: Control+v,Insert
|
||||||
|
|
||||||
|
### **kb-secondary-copy**
|
||||||
|
|
||||||
|
Copy current selection to clipboard
|
||||||
|
|
||||||
|
**Default**: Control+c
|
||||||
|
|
||||||
### **kb-clear-line**
|
### **kb-clear-line**
|
||||||
Clear input line
|
Clear input line
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,8 @@ typedef enum {
|
||||||
PASTE_PRIMARY = 1,
|
PASTE_PRIMARY = 1,
|
||||||
/** Paste from secondary clipboard */
|
/** Paste from secondary clipboard */
|
||||||
PASTE_SECONDARY,
|
PASTE_SECONDARY,
|
||||||
|
/** Copy to secondary clipboard */
|
||||||
|
COPY_SECONDARY,
|
||||||
/** Clear the entry box. */
|
/** Clear the entry box. */
|
||||||
CLEAR_LINE,
|
CLEAR_LINE,
|
||||||
/** Move to front of text */
|
/** Move to front of text */
|
||||||
|
|
|
@ -61,6 +61,7 @@ struct _xcb_stuff {
|
||||||
NkBindingsSeat *bindings_seat;
|
NkBindingsSeat *bindings_seat;
|
||||||
gboolean mouse_seen;
|
gboolean mouse_seen;
|
||||||
xcb_window_t focus_revert;
|
xcb_window_t focus_revert;
|
||||||
|
char *clipboard;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,6 +41,8 @@ typedef struct _xcb_stuff xcb_stuff;
|
||||||
*/
|
*/
|
||||||
extern xcb_stuff *xcb;
|
extern xcb_stuff *xcb;
|
||||||
|
|
||||||
|
void xcb_stuff_set_clipboard(char *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the root window.
|
* 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 */
|
/** Atoms we want to pre-load */
|
||||||
#define EWMH_ATOMS(X) \
|
#define EWMH_ATOMS(X) \
|
||||||
X(_NET_WM_WINDOW_OPACITY), X(I3_SOCKET_PATH), X(UTF8_STRING), X(STRING), \
|
X(_NET_WM_WINDOW_OPACITY), X(I3_SOCKET_PATH), X(TARGETS), X(UTF8_STRING), \
|
||||||
X(CLIPBOARD), X(WM_WINDOW_ROLE), X(_XROOTPMAP_ID), X(_MOTIF_WM_HINTS), \
|
X(STRING), X(CLIPBOARD), X(WM_WINDOW_ROLE), X(_XROOTPMAP_ID), \
|
||||||
X(WM_TAKE_FOCUS), X(ESETROOT_PMAP_ID)
|
X(_MOTIF_WM_HINTS), X(WM_TAKE_FOCUS), X(ESETROOT_PMAP_ID)
|
||||||
|
|
||||||
/** enumeration of the atoms. */
|
/** enumeration of the atoms. */
|
||||||
enum { EWMH_ATOMS(ATOM_ENUM), NUM_NETATOMS };
|
enum { EWMH_ATOMS(ATOM_ENUM), NUM_NETATOMS };
|
||||||
|
|
|
@ -24,12 +24,11 @@
|
||||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <string.h>
|
||||||
#include "nkutils-bindings.h"
|
#include "nkutils-bindings.h"
|
||||||
#include "rofi.h"
|
#include "rofi.h"
|
||||||
#include "xrmoptions.h"
|
#include "xrmoptions.h"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
guint id;
|
guint id;
|
||||||
|
@ -51,6 +50,10 @@ ActionBindingEntry rofi_bindings[] = {
|
||||||
.name = "kb-secondary-paste",
|
.name = "kb-secondary-paste",
|
||||||
.binding = "Control+v,Insert",
|
.binding = "Control+v,Insert",
|
||||||
.comment = "Paste clipboard"},
|
.comment = "Paste clipboard"},
|
||||||
|
{.id = COPY_SECONDARY,
|
||||||
|
.name = "kb-secondary-copy",
|
||||||
|
.binding = "Control+c",
|
||||||
|
.comment = "Copy to clipboard"},
|
||||||
{.id = CLEAR_LINE,
|
{.id = CLEAR_LINE,
|
||||||
.name = "kb-clear-line",
|
.name = "kb-clear-line",
|
||||||
.binding = "Control+w",
|
.binding = "Control+w",
|
||||||
|
|
|
@ -1374,6 +1374,21 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
|
||||||
xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME);
|
xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME);
|
||||||
xcb_flush(xcb->connection);
|
xcb_flush(xcb->connection);
|
||||||
break;
|
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:
|
case SCREENSHOT:
|
||||||
rofi_capture_screenshot();
|
rofi_capture_screenshot();
|
||||||
break;
|
break;
|
||||||
|
@ -2265,6 +2280,8 @@ void rofi_view_hide(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void rofi_view_cleanup() {
|
void rofi_view_cleanup() {
|
||||||
|
// Clear clipboard data.
|
||||||
|
xcb_stuff_set_clipboard(NULL);
|
||||||
g_debug("Cleanup.");
|
g_debug("Cleanup.");
|
||||||
if (CacheState.idle_timeout > 0) {
|
if (CacheState.idle_timeout > 0) {
|
||||||
g_source_remove(CacheState.idle_timeout);
|
g_source_remove(CacheState.idle_timeout);
|
||||||
|
|
65
source/xcb.c
65
source/xcb.c
|
@ -87,7 +87,8 @@ struct _xcb_stuff xcb_int = {.connection = NULL,
|
||||||
.screen_nbr = -1,
|
.screen_nbr = -1,
|
||||||
.sndisplay = NULL,
|
.sndisplay = NULL,
|
||||||
.sncontext = NULL,
|
.sncontext = NULL,
|
||||||
.monitors = NULL};
|
.monitors = NULL,
|
||||||
|
.clipboard = NULL};
|
||||||
xcb_stuff *xcb = &xcb_int;
|
xcb_stuff *xcb = &xcb_int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1216,6 +1217,52 @@ static void main_loop_x11_event_handler_view(xcb_generic_event_t *event) {
|
||||||
}
|
}
|
||||||
break;
|
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: {
|
case XCB_BUTTON_RELEASE: {
|
||||||
xcb_button_release_event_t *bre = (xcb_button_release_event_t *)event;
|
xcb_button_release_event_t *bre = (xcb_button_release_event_t *)event;
|
||||||
NkBindingsMouseButton button;
|
NkBindingsMouseButton button;
|
||||||
|
@ -1257,13 +1304,12 @@ static void main_loop_x11_event_handler_view(xcb_generic_event_t *event) {
|
||||||
gchar *text;
|
gchar *text;
|
||||||
|
|
||||||
xcb->last_timestamp = xkpe->time;
|
xcb->last_timestamp = xkpe->time;
|
||||||
if ( config.xserver_i300_workaround ) {
|
if (config.xserver_i300_workaround) {
|
||||||
text = nk_bindings_seat_handle_key_with_modmask(
|
text = nk_bindings_seat_handle_key_with_modmask(
|
||||||
xcb->bindings_seat, NULL, xkpe->state, xkpe->detail,
|
xcb->bindings_seat, NULL, xkpe->state, xkpe->detail,
|
||||||
NK_BINDINGS_KEY_STATE_PRESS);
|
NK_BINDINGS_KEY_STATE_PRESS);
|
||||||
} else {
|
} else {
|
||||||
text = nk_bindings_seat_handle_key(
|
text = nk_bindings_seat_handle_key(xcb->bindings_seat, NULL, xkpe->detail,
|
||||||
xcb->bindings_seat, NULL, xkpe->detail,
|
|
||||||
NK_BINDINGS_KEY_STATE_PRESS);
|
NK_BINDINGS_KEY_STATE_PRESS);
|
||||||
}
|
}
|
||||||
if (text != NULL) {
|
if (text != NULL) {
|
||||||
|
@ -1294,9 +1340,10 @@ static gboolean main_loop_x11_event_handler(xcb_generic_event_t *ev,
|
||||||
g_main_loop_quit(xcb->main_loop);
|
g_main_loop_quit(xcb->main_loop);
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
// DD: it seems this handler often gets dispatched while the queue in GWater is empty.
|
// DD: it seems this handler often gets dispatched while the queue in GWater
|
||||||
// resulting in a NULL for ev. This seems not an error.
|
// 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);
|
// g_warning("main_loop_x11_event_handler: ev == NULL, status == %d",
|
||||||
|
// status);
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
uint8_t type = ev->response_type & ~0x80;
|
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,
|
xcb_change_window_attributes(xcb->connection, window, XCB_CW_CURSOR,
|
||||||
&(cursors[type]));
|
&(cursors[type]));
|
||||||
}
|
}
|
||||||
|
void xcb_stuff_set_clipboard(char *data) {
|
||||||
|
g_free(xcb->clipboard);
|
||||||
|
xcb->clipboard = data;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue