From 1247e5c4e4c45ede36f846c7fdc854b2a8803a79 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 23 Oct 2018 03:31:34 +0800 Subject: [PATCH 1/3] add new msg dispatch method for filtering events --- source/detail/posix/msg_dispatcher.hpp | 34 ++++++++++++++++++++++---- source/detail/posix/msg_packet.hpp | 4 +-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/source/detail/posix/msg_dispatcher.hpp b/source/detail/posix/msg_dispatcher.hpp index 9ba12ad1..f5b13bb9 100644 --- a/source/detail/posix/msg_dispatcher.hpp +++ b/source/detail/posix/msg_dispatcher.hpp @@ -143,7 +143,7 @@ namespace detail { //Make a cleanup msg packet to infor the dispatcher the window is closed. msg_packet_tag msg; - msg.kind = msg.kind_cleanup; + msg.kind = msg_packet_tag::pkt_family::cleanup; msg.u.packet_window = wd; thr->msg_queue.push_back(msg); } @@ -171,6 +171,30 @@ namespace detail } } } + + template + void dispatch(MsgFilter msg_filter_fn) + { + auto tid = nana::system::this_thread_id(); + msg_packet_tag msg; + int qstate; + + //Test whether the thread is registered for window, and retrieve the queue state for event + while((qstate = _m_read_queue(tid, msg, 0))) + { + //the queue is empty + if(-1 == qstate) + { + if(false == _m_wait_for_queue(tid)) + proc_.timer_proc(tid); + } + else + { + if(msg_filter_fn(msg)) + return; + } + } + } private: void _m_msg_driver() { @@ -220,7 +244,7 @@ namespace detail switch(proc_.filter_proc(event, msg_pack)) { case 0: - msg_pack.kind = msg_pack.kind_xevent; + msg_pack.kind = msg_packet_tag::pkt_family::xevent; msg_pack.u.xevent = event; _m_msg_dispatch(msg_pack); break; @@ -246,9 +270,9 @@ namespace detail { switch(pack.kind) { - case msg_packet_tag::kind_xevent: + case msg_packet_tag::pkt_family::xevent: return _m_event_window(pack.u.xevent); - case msg_packet_tag::kind_mouse_drop: + case msg_packet_tag::pkt_family::mouse_drop: return pack.u.mouse_drop.window; default: break; @@ -294,7 +318,7 @@ namespace detail //Check whether the event dispatcher is used for the modal window //and when the modal window is closing, the event dispatcher would //stop event pumping. - if((modal == msg.u.packet_window) && (msg.kind == msg.kind_cleanup)) + if((modal == msg.u.packet_window) && (msg.kind == msg_packet_tag::pkt_family::cleanup)) return 0; return 1; diff --git a/source/detail/posix/msg_packet.hpp b/source/detail/posix/msg_packet.hpp index ea24091f..fbc0848f 100644 --- a/source/detail/posix/msg_packet.hpp +++ b/source/detail/posix/msg_packet.hpp @@ -10,8 +10,8 @@ namespace detail { struct msg_packet_tag { - enum kind_t{kind_xevent, kind_mouse_drop, kind_cleanup}; - kind_t kind; + enum class pkt_family{xevent, mouse_drop, cleanup}; + pkt_family kind; union { XEvent xevent; From f32f22f429f6664759f542e3a2dbeba9874029aa Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 23 Oct 2018 03:34:12 +0800 Subject: [PATCH 2/3] implement dragdrop for X11 --- source/detail/platform_spec_posix.cpp | 42 ++++- source/detail/posix/platform_spec.hpp | 19 +++ source/gui/detail/bedrock_posix.cpp | 6 +- source/gui/dragdrop.cpp | 229 +++++++++++++++++++++++++- 4 files changed, 285 insertions(+), 11 deletions(-) diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index 62cb293a..46b80f2a 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -1041,6 +1041,12 @@ namespace detail msg_dispatcher_->dispatch(reinterpret_cast(modal)); } + void platform_spec::msg_dispatch(std::function msg_filter_fn) + { + msg_dispatcher_->dispatch(msg_filter_fn); + + } + void* platform_spec::request_selection(native_window_type requestor, Atom type, size_t& size) { if(requestor) @@ -1104,6 +1110,40 @@ namespace detail return graph; } + + bool platform_spec::register_dragdrop(native_window_type wd, dragdrop_interface* ddrop) + { + platform_scope_guard lock; + if(0 != xdnd_.dragdrop.count(wd)) + return false; + + int dndver = 4; + ::XChangeProperty(display_, reinterpret_cast(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, + PropModeReplace, reinterpret_cast(&dndver), 1); + + auto & ref_drop = xdnd_.dragdrop[wd]; + ref_drop.dragdrop = ddrop; + ref_drop.ref_count = 1; + return true; + } + + dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd) + { + platform_scope_guard lock; + auto i = xdnd_.dragdrop.find(wd); + if(i == xdnd_.dragdrop.end()) + return nullptr; + + auto ddrop = i->second; + if(ddrop.ref_count <= 1) + { + xdnd_.dragdrop.erase(i); + return ddrop.dragdrop; + } + --ddrop.ref_count; + return nullptr; + } + //_m_msg_filter //@return: _m_msg_filter returns three states // 0 = msg_dispatcher dispatches the XEvent @@ -1163,7 +1203,7 @@ namespace detail else if(evt.xselection.property == self.atombase_.xdnd_selection) { bool accepted = false; - msg.kind = msg.kind_mouse_drop; + msg.kind = msg_packet_tag::pkt_family::mouse_drop; msg.u.mouse_drop.window = 0; if(bytes_left > 0 && type == self.xdnd_.good_type) { diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index 3191ddb1..af472a92 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -36,6 +36,7 @@ #include #include +#include #include "msg_packet.hpp" #include "../platform_abstraction_types.hpp" @@ -176,6 +177,12 @@ namespace detail ~platform_scope_guard(); }; + class dragdrop_interface + { + public: + virtual ~dragdrop_interface() = default; + }; + class platform_spec { typedef platform_spec self_type; @@ -246,6 +253,7 @@ namespace detail void msg_insert(native_window_type); void msg_set(timer_proc_type, event_proc_type); void msg_dispatch(native_window_type modal); + void msg_dispatch(std::function); //X Selections void* request_selection(native_window_type requester, Atom type, size_t & bufsize); @@ -255,6 +263,9 @@ namespace detail //@biref: The image object should be kept for a long time till the window is closed, // the image object is release in remove() method. const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&); + + bool register_dragdrop(native_window_type, dragdrop_interface*); + dragdrop_interface* remove_dragdrop(native_window_type); private: static int _m_msg_filter(XEvent&, msg_packet_tag&); void _m_caret_routine(); @@ -311,6 +322,14 @@ namespace detail int timestamp; Window wd_src; nana::point pos; + + struct refcount_dragdrop + { + dragdrop_interface* dragdrop{nullptr}; + std::size_t ref_count{0}; + }; + + std::map dragdrop; }xdnd_; msg_dispatcher * msg_dispatcher_; diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 0504cf95..cbee18d2 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -418,10 +418,10 @@ namespace detail { switch(msg.kind) { - case nana::detail::msg_packet_tag::kind_xevent: + case nana::detail::msg_packet_tag::pkt_family::xevent: window_proc_for_xevent(display, msg.u.xevent); break; - case nana::detail::msg_packet_tag::kind_mouse_drop: + case nana::detail::msg_packet_tag::pkt_family::mouse_drop: window_proc_for_packet(display, msg); break; default: break; @@ -441,7 +441,7 @@ namespace detail switch(msg.kind) { - case nana::detail::msg_packet_tag::kind_mouse_drop: + case nana::detail::msg_packet_tag::pkt_family::mouse_drop: msgwd = brock.wd_manager().find_window(native_window, {msg.u.mouse_drop.x, msg.u.mouse_drop.y}); if(msgwd) { diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index a267890b..a4323dc2 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -10,6 +10,7 @@ * @file: nana/gui/dragdrop.cpp * @author: Jinhao(cnjinhao@hotmail.com) */ + #include #include @@ -24,6 +25,11 @@ # include # include # include +#elif defined(NANA_X11) +# include "../detail/posix/platform_spec.hpp" +# include +# include +# include #endif namespace nana @@ -367,6 +373,60 @@ namespace nana private: std::vector mediums_; }; +#elif defined(NANA_X11) + class x11_dragdrop: public detail::dragdrop_interface + { + + }; + + class shared_icons + { + public: + shared_icons() + { + path_ = "/usr/share/icons/"; + ifs_.open(path_ + "default/index.theme"); + } + + std::string cursor(const std::string& name) + { + auto theme = _m_read("Icon Theme", "Inherits"); + + return path_ + theme + "/cursors/" + name; + } + private: + std::string _m_read(const std::string& category, const std::string& key) + { + ifs_.seekg(0, std::ios::beg); + + bool found_cat = false; + while(ifs_.good()) + { + std::string text; + std::getline(ifs_, text); + + if(0 == text.find('[')) + { + if(found_cat) + break; + + if(text.find(category + "]") != text.npos) + { + found_cat = true; + } + } + else if(found_cat && (text.find(key + "=") == 0)) + { + return text.substr(key.size() + 1); + } + } + + return {}; + } + private: + std::string path_; + std::ifstream ifs_; + }; #endif class dragdrop_service @@ -385,6 +445,7 @@ namespace nana if (nullptr == native_wd) return; +#ifdef NANA_WINDOWS if(table_.empty()) ::OleInitialize(nullptr); @@ -403,22 +464,33 @@ namespace nana drop_target = i->second; drop_target->AddRef(); } +#elif defined(NANA_X11) + auto ddrop = new x11_dragdrop; + if(!_m_spec().register_dragdrop(native_wd, ddrop)) + delete ddrop; +#endif } void remove(window wd) { +#ifdef NANA_WINDOWS auto i = table_.find(API::root(wd)); if (i != table_.end()) { if (0 == i->second->Release()) table_.erase(i); } - +#elif defined(NANA_X11) + auto ddrop = _m_spec().remove_dragdrop(API::root(wd)); + delete ddrop; +#endif drop_assoc_.erase(wd); + } bool dragdrop(window drag_wd) { +#ifdef NANA_WINDOWS auto i = table_.find(API::root(drag_wd)); if (table_.end() == i) return false; @@ -438,19 +510,158 @@ namespace nana auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff); i->second->set_source(nullptr); + return true; +#elif defined(NANA_X11) + auto const native_wd = reinterpret_cast(API::root(drag_wd)); + + { + detail::platform_scope_guard lock; + ::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_wd, CurrentTime); + } + + + hovered_.window_handle = nullptr; + hovered_.native_wd = 0; + window target_wd = 0; + auto& atombase = _m_spec().atombase(); + //while(true) + { + + _m_spec().msg_dispatch([this, drag_wd, native_wd, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ + if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind) + { + auto const disp = _m_spec().open_display(); + if (MotionNotify == msg_pkt.u.xevent.type) + { + auto pos = API::cursor_position(); + auto native_cur_wd = reinterpret_cast(detail::native_interface::find_window(pos.x, pos.y)); + + if(hovered_.native_wd != native_cur_wd) + { + if(hovered_.native_wd) + { + _m_free_cursor(); + ::XUndefineCursor(disp, hovered_.native_wd); + } + + _m_client_msg(native_cur_wd, native_wd, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); + hovered_.native_wd = native_cur_wd; + } + + auto cur_wd = API::find_window(API::cursor_position()); + + if(hovered_.window_handle != cur_wd) + { + _m_free_cursor(); + + hovered_.window_handle = cur_wd; + + if((drag_wd == cur_wd) || drop_assoc_.has(drag_wd, cur_wd)) + hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-move").c_str()); + else + hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-none").c_str()); + ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); + } + } + else if(msg_pkt.u.xevent.type == ButtonRelease) + { + target_wd = API::find_window(API::cursor_position()); + ::XUndefineCursor(disp, hovered_.native_wd); + _m_free_cursor(); + return true; + } + + } + return false; + }); + } + + return (nullptr != target_wd); +#endif + return false; } drop_association& drop_assoc() { return drop_assoc_; } +#ifdef NANA_X11 + private: + static nana::detail::platform_spec & _m_spec() + { + return nana::detail::platform_spec::instance(); + } + + //dndversion<<24, fl_XdndURIList, XA_STRING, 0 + static void _m_client_msg(Window wd_target, Window wd_src, Atom xdnd_atom, Atom data, Atom data_type) + { + auto const display = _m_spec().open_display(); + XEvent evt; + ::memset(&evt, 0, sizeof evt); + evt.xany.type = ClientMessage; + evt.xany.display = display; + evt.xclient.window = wd_target; + evt.xclient.message_type = xdnd_atom; + evt.xclient.format = 32; + + //Target window + evt.xclient.data.l[0] = wd_src; + //Accept set + evt.xclient.data.l[1] = 1; + evt.xclient.data.l[2] = data; + evt.xclient.data.l[3] = data_type; + evt.xclient.data.l[4] = 0; + + ::XSendEvent(display, wd_target, True, NoEventMask, &evt); + + } + + static int _m_xdnd_aware(Window wd) + { + Atom actual; int format; unsigned long count, remaining; + unsigned char *data = 0; + XGetWindowProperty(_m_spec().open_display(), wd, _m_spec().atombase().xdnd_aware, + 0, 4, False, XA_ATOM, &actual, &format, &count, &remaining, &data); + + int version = 0; + if ((actual == XA_ATOM) && (format==32) && count && data) + version = int(*(Atom*)data); + + if (data) + ::XFree(data); + return version; + } + + void _m_free_cursor() + { + if(hovered_.cursor) + { + ::XFreeCursor(_m_spec().open_display(), hovered_.cursor); + hovered_.cursor = 0; + } + + } +#endif private: - std::map table_; drop_association drop_assoc_; +#ifdef NANA_WINDOWS + std::map table_; +#elif defined (NANA_X11) + shared_icons icons_; + struct hovered_status + { + Window native_wd{0}; + window window_handle{nullptr}; + + unsigned shape{0}; + Cursor cursor{0}; + }hovered_; +#endif }; + struct simple_dragdrop::implementation { window window_handle; @@ -459,7 +670,7 @@ namespace nana bool dragging{ false }; -#if 0 +#ifdef NANA_X11 bool cancel() { if (!dragging) @@ -499,11 +710,15 @@ namespace nana auto & events = API::events<>(drag_wd); -#ifdef NANA_WINDOWS +#if 1 //#ifdef NANA_WINDOWS events.mouse_down.connect_unignorable([this](const arg_mouse& arg){ if (arg.is_left_button() && API::is_window(impl_->window_handle)) { impl_->dragging = ((!impl_->predicate) || impl_->predicate()); + + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(impl_->window_handle); + real_wd->other.dnd_state = dragdrop_status::ready; } }); @@ -528,11 +743,11 @@ namespace nana i->second(); } }); -#else +#elif 1 events.mouse_down.connect_unignorable([drag_wd](const arg_mouse& arg){ if (arg.is_left_button() && API::is_window(drag_wd)) { - API::set_capture(drag_wd, true); + //API::set_capture(drag_wd, true); using basic_window = ::nana::detail::basic_window; auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(drag_wd); @@ -566,7 +781,7 @@ namespace nana } } - API::release_capture(impl_->window_handle); + //API::release_capture(impl_->window_handle); } }); From ff9b90a76650ed79f47cd341c4bf8725bb97157a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 23 Oct 2018 03:40:54 +0800 Subject: [PATCH 3/3] add libXcursor-dev --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index d98c0dcf..47cc31ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ matrix: - alsa-oss - libx11-dev - libxft-dev + - libxcursor-dev sources: - ubuntu-toolchain-r-test @@ -36,6 +37,7 @@ matrix: - alsa-oss - libx11-dev - libxft-dev + - libxcursor-dev sources: - ubuntu-toolchain-r-test - llvm-toolchain-precise