Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added primary clipboard for Linux #53702

Merged
merged 1 commit into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,28 @@
Returns the user's clipboard as a string if possible.
</description>
</method>
<method name="clipboard_get_primary" qualifiers="const">
<return type="String" />
<description>
Returns the user's primary clipboard as a string if possible.
[b]Note:[/b] This method is only implemented on Linux.
</description>
</method>
<method name="clipboard_set">
<return type="void" />
<argument index="0" name="clipboard" type="String" />
<description>
Sets the user's clipboard content to the given string.
</description>
</method>
<method name="clipboard_set_primary">
<return type="void" />
<argument index="0" name="clipboard_primary" type="String" />
<description>
Sets the user's primary clipboard content to the given string.
[b]Note:[/b] This method is only implemented on Linux.
</description>
</method>
<method name="console_set_visible">
<return type="void" />
<argument index="0" name="console_visible" type="bool" />
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/LineEdit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@
[/csharp]
[/codeblocks]
</member>
<member name="middle_mouse_paste_enabled" type="bool" setter="set_middle_mouse_paste_enabled" getter="is_middle_mouse_paste_enabled" default="true">
If [code]false[/code], using middle mouse button to paste clipboard will be disabled.
[b]Note:[/b] This method is only implemented on Linux.
</member>
<member name="mouse_default_cursor_shape" type="int" setter="set_default_cursor_shape" getter="get_default_cursor_shape" override="true" enum="Control.CursorShape" default="1" />
<member name="placeholder_alpha" type="float" setter="set_placeholder_alpha" getter="get_placeholder_alpha" default="0.6">
Opacity of the [member placeholder_text]. From [code]0[/code] to [code]1[/code].
Expand Down
11 changes: 11 additions & 0 deletions doc/classes/TextEdit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@
Override this method to define what happens when the user performs a paste operation.
</description>
</method>
<method name="_paste_primary_clipboard" qualifiers="virtual">
<return type="void" />
<description>
Override this method to define what happens when the user performs a paste operation with middle mouse button.
[b]Note:[/b] This method is only implemented on Linux.
</description>
</method>
<method name="add_gutter">
<return type="void" />
<argument index="0" name="at" type="int" default="-1" />
Expand Down Expand Up @@ -936,6 +943,10 @@
<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
</member>
<member name="middle_mouse_paste_enabled" type="bool" setter="set_middle_mouse_paste_enabled" getter="is_middle_mouse_paste_enabled" default="true">
If [code]false[/code], using middle mouse button to paste clipboard will be disabled.
[b]Note:[/b] This method is only implemented on Linux.
</member>
<member name="minimap_draw" type="bool" setter="set_draw_minimap" getter="is_drawing_minimap" default="false">
If [code]true[/code], a minimap is shown, providing an outline of your source code.
</member>
Expand Down
48 changes: 43 additions & 5 deletions platform/linuxbsd/display_server_x11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,20 @@ void DisplayServerX11::clipboard_set(const String &p_text) {
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}

void DisplayServerX11::clipboard_set_primary(const String &p_text) {
_THREAD_SAFE_METHOD_
if (!p_text.is_empty()) {
{
// The clipboard content can be accessed while polling for events.
MutexLock mutex_lock(events_mutex);
internal_clipboard_primary = p_text;
}

XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "PRIMARY", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}
}

Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) {
if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) {
return True;
Expand All @@ -427,7 +441,12 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A

Window selection_owner = XGetSelectionOwner(x11_display, p_source);
if (selection_owner == x11_window) {
return internal_clipboard;
static const char *target_type = "PRIMARY";
if (p_source != None && String(XGetAtomName(x11_display, p_source)) == target_type) {
return internal_clipboard_primary;
} else {
return internal_clipboard;
}
}

if (selection_owner != None) {
Expand Down Expand Up @@ -580,6 +599,19 @@ String DisplayServerX11::clipboard_get() const {
return ret;
}

String DisplayServerX11::clipboard_get_primary() const {
_THREAD_SAFE_METHOD_

String ret;
ret = _clipboard_get(XInternAtom(x11_display, "PRIMARY", 0), windows[MAIN_WINDOW_ID].x11_window);

if (ret.is_empty()) {
ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window);
}

return ret;
}

Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) {
if (event->xany.window == *(Window *)arg) {
return (event->type == SelectionRequest) ||
Expand Down Expand Up @@ -2417,7 +2449,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
Input::get_singleton()->parse_input_event(k);
}

Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const {
Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) const {
if (p_target == XInternAtom(x11_display, "TARGETS", 0)) {
// Request to list all supported targets.
Atom data[9];
Expand Down Expand Up @@ -2459,7 +2491,13 @@ Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p
p_target == XInternAtom(x11_display, "text/plain", 0)) {
// Directly using internal clipboard because we know our window
// is the owner during a selection request.
CharString clip = internal_clipboard.utf8();
CharString clip;
static const char *target_type = "PRIMARY";
if (p_selection != None && String(XGetAtomName(x11_display, p_selection)) == target_type) {
clip = internal_clipboard_primary.utf8();
} else {
clip = internal_clipboard.utf8();
}
XChangeProperty(x11_display,
p_requestor,
p_property,
Expand Down Expand Up @@ -2497,7 +2535,7 @@ void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p
for (uint64_t i = 0; i < len; i += 2) {
Atom target = targets[i];
Atom &property = targets[i + 1];
property = _process_selection_request_target(target, p_event->requestor, property);
property = _process_selection_request_target(target, p_event->requestor, property, p_event->selection);
}

XChangeProperty(x11_display,
Expand All @@ -2515,7 +2553,7 @@ void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p
}
} else {
// Request for target conversion.
respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property);
respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property, p_event->selection);
}

respond.xselection.type = SelectionNotify;
Expand Down
5 changes: 4 additions & 1 deletion platform/linuxbsd/display_server_x11.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class DisplayServerX11 : public DisplayServer {
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);

String internal_clipboard;
String internal_clipboard_primary;
Window xdnd_source_window;
::Display *x11_display;
char *xmbstring;
Expand Down Expand Up @@ -205,7 +206,7 @@ class DisplayServerX11 : public DisplayServer {

void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);

Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const;
Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) const;
void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;

String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
Expand Down Expand Up @@ -290,6 +291,8 @@ class DisplayServerX11 : public DisplayServer {

virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
virtual void clipboard_set_primary(const String &p_text) override;
virtual String clipboard_get_primary() const override;

virtual int get_screen_count() const override;
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
Expand Down
39 changes: 39 additions & 0 deletions scene/gui/line_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,25 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}

if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_MIDDLE && is_editable()) {
String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes();

deselect();
set_caret_at_pixel_pos(b->get_position().x);
if (!paste_buffer.is_empty()) {
insert_text_at_caret(paste_buffer);

if (!text_changed_dirty) {
if (is_inside_tree()) {
MessageQueue::get_singleton()->push_call(this, "_text_changed");
}
text_changed_dirty = true;
}
}
grab_focus();
return;
}

if (b->get_button_index() != MOUSE_BUTTON_LEFT) {
return;
}
Expand Down Expand Up @@ -271,6 +290,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
selection.double_click = true;
last_dblclk = 0;
caret_column = selection.begin;
if (!pass) {
DisplayServer::get_singleton()->clipboard_set_primary(text);
}
} else if (b->is_double_click()) {
// Double-click select word.
last_dblclk = OS::get_singleton()->get_ticks_msec();
Expand All @@ -286,6 +308,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
break;
}
}
if (!pass) {
DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin));
}
}
}

Expand All @@ -303,6 +328,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
update();

} else {
if (selection.enabled && !pass && b->get_button_index() == MOUSE_BUTTON_LEFT) {
DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin));
}
if (!text.is_empty() && is_editable() && clear_button_enabled) {
bool press_attempt = clear_button_status.press_attempt;
clear_button_status.press_attempt = false;
Expand Down Expand Up @@ -1890,6 +1918,14 @@ bool LineEdit::is_virtual_keyboard_enabled() const {
return virtual_keyboard_enabled;
}

void LineEdit::set_middle_mouse_paste_enabled(bool p_enabled) {
middle_mouse_paste_enabled = p_enabled;
}

bool LineEdit::is_middle_mouse_paste_enabled() const {
return middle_mouse_paste_enabled;
}

void LineEdit::set_selecting_enabled(bool p_enabled) {
selecting_enabled = p_enabled;

Expand Down Expand Up @@ -2156,6 +2192,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled);
ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &LineEdit::set_shortcut_keys_enabled);
ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &LineEdit::is_shortcut_keys_enabled);
ClassDB::bind_method(D_METHOD("set_middle_mouse_paste_enabled", "enable"), &LineEdit::set_middle_mouse_paste_enabled);
ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &LineEdit::is_middle_mouse_paste_enabled);
ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled);
ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled);
ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon);
Expand Down Expand Up @@ -2211,6 +2249,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
Expand Down
5 changes: 5 additions & 0 deletions scene/gui/line_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class LineEdit : public Control {

bool virtual_keyboard_enabled = true;

bool middle_mouse_paste_enabled = true;

Ref<Texture2D> right_icon;

struct Selection {
Expand Down Expand Up @@ -318,6 +320,9 @@ class LineEdit : public Control {
void set_virtual_keyboard_enabled(bool p_enable);
bool is_virtual_keyboard_enabled() const;

void set_middle_mouse_paste_enabled(bool p_enabled);
bool is_middle_mouse_paste_enabled() const;

void set_selecting_enabled(bool p_enabled);
bool is_selecting_enabled() const;

Expand Down
5 changes: 5 additions & 0 deletions scene/gui/rich_text_label.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1596,12 +1596,16 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
selection.to_char = words[i + 1];

selection.active = true;
DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
update();
break;
}
}
}
} else if (!b->is_pressed()) {
if (selection.enabled) {
DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
}
selection.click_item = nullptr;

if (!b->is_double_click() && !scroll_updated) {
Expand Down Expand Up @@ -1719,6 +1723,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
swap = true;
} else if (selection.from_char == selection.to_char) {
selection.active = false;
update();
return;
}
}
Expand Down
Loading