Skip to content

Commit

Permalink
Added primary clipboard for Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
ConteZero committed Mar 14, 2022
1 parent c2c0c80 commit 2ff0735
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 7 deletions.
8 changes: 8 additions & 0 deletions core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ bool OS::has_clipboard() const {
return !get_clipboard().empty();
}

void OS::set_clipboard_primary(const String &p_text) {
_primary_clipboard = p_text;
}

String OS::get_clipboard_primary() const {
return _primary_clipboard;
}

String OS::get_executable_path() const {
return _execpath;
}
Expand Down
3 changes: 3 additions & 0 deletions core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class OS {
bool _verbose_stdout;
bool _debug_stdout;
String _local_clipboard;
String _primary_clipboard;
uint64_t _msec_splash;
bool _no_window;
int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure
Expand Down Expand Up @@ -179,6 +180,8 @@ class OS {
virtual void set_clipboard(const String &p_text);
virtual String get_clipboard() const;
virtual bool has_clipboard() const;
virtual void set_clipboard_primary(const String &p_text);
virtual String get_clipboard_primary() const;

virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0) = 0;
virtual VideoMode get_video_mode(int p_screen = 0) const = 0;
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/LineEdit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@
# `text_change_rejected` is emitted with "bye" as parameter.
[/codeblock]
</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" overrides="Control" 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
4 changes: 4 additions & 0 deletions doc/classes/TextEdit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@
<member name="highlight_current_line" type="bool" setter="set_highlight_current_line" getter="is_highlight_current_line_enabled" default="false">
If [code]true[/code], the line containing the cursor is highlighted.
</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="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
1 change: 1 addition & 0 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,7 @@ void Main::cleanup(bool p_force) {
OS::get_singleton()->_cmdline.clear();
OS::get_singleton()->_execpath = "";
OS::get_singleton()->_local_clipboard = "";
OS::get_singleton()->_primary_clipboard = "";

ResourceLoader::clear_translation_remaps();
ResourceLoader::clear_path_remaps();
Expand Down
55 changes: 49 additions & 6 deletions platform/x11/os_x11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2177,7 +2177,7 @@ void OS_X11::_handle_key_event(XKeyEvent *p_event, LocalVector<XEvent> &p_events
input->parse_input_event(k);
}

Atom OS_X11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const {
Atom OS_X11::_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 @@ -2220,7 +2220,13 @@ Atom OS_X11::_process_selection_request_target(Atom p_target, Window p_requestor
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 = OS::get_clipboard().utf8();
CharString clip;
static const char *target_type = "PRIMARY";
if (p_selection != None && String(XGetAtomName(x11_display, p_selection)) == target_type) {
clip = OS::get_clipboard_primary().utf8();
} else {
clip = OS::get_clipboard().utf8();
}
XChangeProperty(x11_display,
p_requestor,
p_property,
Expand Down Expand Up @@ -2258,7 +2264,7 @@ void OS_X11::_handle_selection_request_event(XSelectionRequestEvent *p_event) co
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 @@ -2276,7 +2282,7 @@ void OS_X11::_handle_selection_request_event(XSelectionRequestEvent *p_event) co
}
} 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 Expand Up @@ -3112,7 +3118,12 @@ String OS_X11::_get_clipboard_impl(Atom p_source, Window x11_window, Atom target

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

if (selection_owner != None) {
Expand Down Expand Up @@ -3315,6 +3326,30 @@ void OS_X11::_clipboard_transfer_ownership(Atom p_source, Window x11_window) con
}
}

void OS_X11::set_clipboard_primary(const String &p_text) {
if (!p_text.empty()) {
{
// The clipboard content can be accessed while polling for events.
MutexLock mutex_lock(events_mutex);
OS::set_clipboard_primary(p_text);
}

XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime);
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "PRIMARY", 0), x11_window, CurrentTime);
}
}

String OS_X11::get_clipboard_primary() const {
String ret;
ret = _get_clipboard(XInternAtom(x11_display, "PRIMARY", 0), x11_window);

if (ret.empty()) {
ret = _get_clipboard(XA_PRIMARY, x11_window);
}

return ret;
}

String OS_X11::get_name() const {
return "X11";
}
Expand Down Expand Up @@ -3357,7 +3392,15 @@ Error OS_X11::shell_open(String p_uri) {
}

bool OS_X11::_check_internal_feature_support(const String &p_feature) {
return p_feature == "pc";
if (p_feature == "pc") {
return true;
}

if (p_feature == "primary_clipboard") {
return true;
}

return false;
}

String OS_X11::get_config_path() const {
Expand Down
4 changes: 3 additions & 1 deletion platform/x11/os_x11.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class OS_X11 : public OS_Unix {

void _handle_key_event(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 _get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const;
Expand Down Expand Up @@ -273,6 +273,8 @@ class OS_X11 : public OS_Unix {

virtual void set_clipboard(const String &p_text);
virtual String get_clipboard() const;
virtual void set_clipboard_primary(const String &p_text);
virtual String get_clipboard_primary() const;

virtual void release_rendering_thread();
virtual void make_rendering_thread();
Expand Down
41 changes: 41 additions & 0 deletions scene/gui/line_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,26 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
return;
}

if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == BUTTON_MIDDLE && is_editable() && OS::get_singleton()->has_feature("primary_clipboard")) {
String paste_buffer = OS::get_singleton()->get_clipboard_primary().strip_escapes();

selection.enabled = false;
set_cursor_at_pixel_pos(b->get_position().x);
if (!paste_buffer.empty()) {
append_at_cursor(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() != BUTTON_LEFT) {
return;
}
Expand Down Expand Up @@ -100,6 +120,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
selection.end = text.length();
selection.doubleclick = true;
selection.last_dblclk = 0;
if (!pass && OS::get_singleton()->has_feature("primary_clipboard")) {
OS::get_singleton()->set_clipboard_primary(text);
}
} else if (b->is_doubleclick()) {
// Double-click select word.
selection.enabled = true;
Expand All @@ -119,6 +142,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
selection.end = end;
selection.doubleclick = true;
selection.last_dblclk = OS::get_singleton()->get_ticks_msec();
if (!pass && OS::get_singleton()->has_feature("primary_clipboard")) {
OS::get_singleton()->set_clipboard_primary(text.substr(selection.begin, selection.end - selection.begin));
}
}
}

Expand All @@ -136,6 +162,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
update();

} else {
if (selection.enabled && !pass && b->get_button_index() == BUTTON_LEFT && OS::get_singleton()->has_feature("primary_clipboard")) {
OS::get_singleton()->set_clipboard_primary(text.substr(selection.begin, selection.end - selection.begin));
}
if (!text.empty() && is_editable() && clear_button_enabled) {
bool press_attempt = clear_button_status.press_attempt;
clear_button_status.press_attempt = false;
Expand Down Expand Up @@ -1779,6 +1808,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 @@ -1955,6 +1992,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_deselect_on_focus_loss_enabled", "enable"), &LineEdit::set_deselect_on_focus_loss_enabled);
Expand Down Expand Up @@ -1991,6 +2030,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::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
Expand Down Expand Up @@ -2019,6 +2059,7 @@ LineEdit::LineEdit() {
clear_button_status.press_attempt = false;
clear_button_status.pressing_inside = false;
shortcut_keys_enabled = true;
middle_mouse_paste_enabled = true;
selecting_enabled = true;
deselect_on_focus_loss_enabled = true;

Expand Down
4 changes: 4 additions & 0 deletions scene/gui/line_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class LineEdit : public Control {

bool drag_action = false;
bool drag_caret_force_displayed = false;
bool middle_mouse_paste_enabled;

Ref<Texture> right_icon;

Expand Down Expand Up @@ -239,6 +240,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
7 changes: 7 additions & 0 deletions scene/gui/rich_text_label.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
selection.from_char = beg;
selection.to_char = end - 1;
selection.active = true;
if (OS::get_singleton()->has_feature("primary_clipboard")) {
OS::get_singleton()->set_clipboard_primary(get_selected_text());
}
update();
}
}
Expand All @@ -1228,6 +1231,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
update();
}
}
if (selection.enabled && OS::get_singleton()->has_feature("primary_clipboard")) {
OS::get_singleton()->set_clipboard_primary(get_selected_text());
}
selection.click = nullptr;

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

0 comments on commit 2ff0735

Please sign in to comment.