1417 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1417 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *	Platform Specification Implementation
 | |
|  *	Nana C++ Library(http://www.nanapro.org)
 | |
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
 | |
|  *
 | |
|  *	Distributed under the Nana Software License, Version 1.0.
 | |
|  *	(See accompanying file LICENSE_1_0.txt or copy at
 | |
|  *	http://nanapro.org/LICENSE_1_0.txt)
 | |
|  *
 | |
|  *	@file: nana/detail/linux_X11/platform_spec.cpp
 | |
|  *
 | |
|  *	This file provides basis class and data structrue that required by nana
 | |
|  *
 | |
|  *	http://standards.freedesktop.org/clipboards-spec/clipboards-0.1.txt
 | |
|  */
 | |
| 
 | |
| #include "platform_spec_selector.hpp"
 | |
| #include "platform_abstraction.hpp"
 | |
| #if defined(NANA_POSIX) && defined(NANA_X11)
 | |
| 
 | |
| #include <nana/push_ignore_diagnostic>
 | |
| 
 | |
| #include <X11/Xlocale.h>
 | |
| #include <clocale>
 | |
| #include <map>
 | |
| #include <set>
 | |
| #include <algorithm>
 | |
| #include <nana/paint/graphics.hpp>
 | |
| #include <nana/gui/detail/bedrock.hpp>
 | |
| #include <nana/gui/detail/basic_window.hpp>
 | |
| #include <nana/gui/detail/window_manager.hpp>
 | |
| #include <nana/system/platform.hpp>
 | |
| #include <nana/paint/pixel_buffer.hpp>
 | |
| #include <errno.h>
 | |
| #include <sstream>
 | |
| 
 | |
| #include "posix/msg_dispatcher.hpp"
 | |
| 
 | |
| namespace nana
 | |
| {
 | |
| namespace detail
 | |
| {
 | |
| 	typedef native_window_type native_window_type;
 | |
| #if defined(NANA_USE_XFT)
 | |
| 	//class conf
 | |
| 		conf::conf(const char * file)
 | |
| 		{
 | |
| 			ifs_.open(file);
 | |
| 		}
 | |
| 
 | |
| 		bool conf::open(const char* file)
 | |
| 		{
 | |
| 			ifs_.open(file);
 | |
| 			return static_cast<bool>(ifs_);
 | |
| 		}
 | |
| 
 | |
| 		std::string conf::value(const char* key)
 | |
| 		{
 | |
| 			if((0 == key) || !ifs_) return "";
 | |
| 			size_t len = ::strlen(key);
 | |
| 			ifs_.seekg(0, std::ios::beg);
 | |
| 			ifs_.clear();
 | |
| 			std::string str;
 | |
| 
 | |
| 			while(ifs_.good())
 | |
| 			{
 | |
| 				std::getline(ifs_, str);
 | |
| 				if(str.size() <= len + 1)
 | |
| 					continue;
 | |
| 
 | |
| 				size_t kpos = str.find(key);
 | |
| 				if((kpos != str.npos) && ((kpos == 0) || (str.substr(0, kpos) == std::string(kpos, ' '))))
 | |
| 				{
 | |
| 					size_t aspos = str.find("=", kpos + len);
 | |
| 					if(aspos != str.npos)
 | |
| 					{
 | |
| 						if((aspos == kpos + len) || (str.substr(kpos + len, aspos) == std::string(aspos - kpos - len, ' ')))
 | |
| 						{
 | |
| 							std::string res = str.substr(aspos + 1);
 | |
| 							size_t beg = res.find_first_not_of(" ");
 | |
| 							if(beg && (beg != res.npos))
 | |
| 								res = res.substr(beg);
 | |
| 							beg = res.find("\"");
 | |
| 							if(beg == 0)
 | |
| 							{
 | |
| 								size_t end = res.find_last_of("\"");
 | |
| 								if(beg != end)
 | |
| 									return res.substr(beg + 1, (end == res.npos ? res.npos : (end - 1)));
 | |
| 							}
 | |
| 							return res;
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return std::string();
 | |
| 		}
 | |
| 	//end class conf
 | |
| 
 | |
| 	//class charset_conv
 | |
| 		charset_conv::charset_conv(const char* tocode, const char* fromcode)
 | |
| 		{
 | |
| 			handle_ = ::iconv_open(tocode, fromcode);
 | |
| 		}
 | |
| 
 | |
| 		charset_conv::~charset_conv()
 | |
| 		{
 | |
| 			::iconv_close(handle_);
 | |
| 		}
 | |
| 
 | |
| 		std::string charset_conv::charset(const std::string& str) const
 | |
| 		{
 | |
| 			if(reinterpret_cast<iconv_t>(-1) == handle_)
 | |
| 				return std::string();
 | |
| 
 | |
| 			char * inbuf = const_cast<char*>(str.c_str());
 | |
| 			std::size_t inleft = str.size();
 | |
| 			std::size_t outlen = (inleft * 4 + 4);
 | |
| 			char * strbuf = new char[outlen + 4];
 | |
| 			char * outbuf = strbuf;
 | |
| 			std::size_t outleft = outlen;
 | |
| 			::iconv(handle_, &inbuf, &inleft, &outbuf, &outleft);
 | |
| 			std::string rstr(strbuf, outbuf);
 | |
| 			delete [] strbuf;
 | |
| 			return rstr;
 | |
| 		}
 | |
| 
 | |
| 		std::string charset_conv::charset(const char* buf, std::size_t len) const
 | |
| 		{
 | |
| 			if(reinterpret_cast<iconv_t>(-1) == handle_)
 | |
| 				return std::string();
 | |
| 
 | |
| 			char * inbuf = const_cast<char*>(buf);
 | |
| 			std::size_t outlen = (len * 4 + 4);
 | |
| 			char * strbuf = new char[outlen + 4];
 | |
| 			char * outbuf = strbuf;
 | |
| 			std::size_t outleft = outlen;
 | |
| 			::iconv(handle_, &inbuf, &len, &outbuf, &outleft);
 | |
| 			std::string rstr(strbuf, outbuf);
 | |
| 			delete [] strbuf;
 | |
| 			return rstr;
 | |
| 		}
 | |
| 	//end class charset_conv
 | |
| #endif
 | |
| 
 | |
| 	//Caret implementation
 | |
| 	struct caret_rep
 | |
| 	{
 | |
| 		native_window_type window;
 | |
| 		bool has_input_method_focus{ false };
 | |
| 		bool visible{ false };
 | |
| 		nana::point pos;
 | |
| 		nana::size	size;
 | |
| 		nana::rectangle rev;
 | |
| 		nana::paint::graphics rev_graph;
 | |
| 		XIM	input_method{ 0 };
 | |
| 		XIC	input_context{ 0 };
 | |
| 		XFontSet input_font{ 0 };
 | |
| 		XRectangle input_spot;
 | |
| 		XRectangle input_status_area;
 | |
| 		long input_context_event_mask{ 0 };
 | |
| 
 | |
| 		caret_rep(native_window_type wd)
 | |
| 			: window{ wd }
 | |
| 		{}
 | |
| 
 | |
| 		//Copy the reversed graphics to the window
 | |
| 		bool reinstate()
 | |
| 		{
 | |
| 			if(rev.width && rev.height)
 | |
| 			{
 | |
| 				rev_graph.paste(window, rev, 0, 0);
 | |
| 				//Drop the reversed graphics in order to draw the
 | |
| 				//caret in the next flash.
 | |
| 				rev.width = rev.height = 0;
 | |
| 				return true;
 | |
| 			}
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		void twinkle()
 | |
| 		{
 | |
| 			if(!visible)
 | |
| 				return;
 | |
| 
 | |
| 			if(!reinstate())
 | |
| 			{
 | |
| 				rev_graph.bitblt(rectangle{size}, window, pos);
 | |
| 				rev.width = size.width;
 | |
| 				rev.height = size.height;
 | |
| 				rev.x = pos.x;
 | |
| 				rev.y = pos.y;
 | |
| 
 | |
| 				paint::pixel_buffer pxbuf;
 | |
| 				pxbuf.open(rev_graph.handle());
 | |
| 
 | |
| 				auto pxsize = pxbuf.size();
 | |
| 				for(int y = 0; y < static_cast<int>(pxsize.height); ++y)
 | |
| 					for(int x = 0; x < static_cast<int>(pxsize.width); ++x)
 | |
| 					{
 | |
| 						auto px = pxbuf.at({x, y});
 | |
| 						px->element.red = ~px->element.red;
 | |
| 						px->element.green = ~px->element.green;
 | |
| 						px->element.blue = ~px->element.blue;
 | |
| 					}
 | |
| 				pxbuf.paste(window, {rev.x, rev.y});
 | |
| 			}
 | |
| 		}
 | |
| 	};
 | |
| 
 | |
| 	class timer_runner
 | |
| 	{
 | |
| 		typedef void (*timer_proc_t)(std::size_t id);
 | |
| 
 | |
| 		struct timer_tag
 | |
| 		{
 | |
| 			std::size_t id;
 | |
| 			thread_t tid;
 | |
| 			std::size_t interval;
 | |
| 			std::size_t timestamp;
 | |
| 			timer_proc_t proc;
 | |
| 		};
 | |
| 
 | |
| 		//timer_group
 | |
| 		//It owns a set of timers' identifier, and a container for the delay deletion
 | |
| 		//The delay delection is used for storing a timer id when the timer is deleted in a timer's
 | |
| 		//event handler function. If the timer is deleted directly in timer's event handler function,
 | |
| 		//it will cause a crash because the deletion operation invalidates iterator.
 | |
| 		//According to ISO C++ 2011, 23.2.4 9 the erase members shall invalidate only iterators and
 | |
| 		//references to the erased elements(timer_group::timers is an associative container),
 | |
| 		//although the iterator can be moved to next before calling the timer handler function, the delay
 | |
| 		//deletion is still required. Becuase a timer which is erased in another timer's handler function
 | |
| 		//happens to be refereneced by the "next" iterator.
 | |
| 		struct timer_group
 | |
| 		{
 | |
| 			bool proc_entered{false};	//This flag indicates whether the timers are going to do event.
 | |
| 			std::set<std::size_t> timers;
 | |
| 			std::vector<std::size_t> delay_deleted;
 | |
| 		};
 | |
| 	public:
 | |
| 		timer_runner()
 | |
| 			: is_proc_handling_(false)
 | |
| 		{}
 | |
| 
 | |
| 		void set(std::size_t id, std::size_t interval, timer_proc_t proc)
 | |
| 		{
 | |
| 			auto i = holder_.find(id);
 | |
| 			if(i != holder_.end())
 | |
| 			{
 | |
| 				i->second.interval = interval;
 | |
| 				i->second.proc = proc;
 | |
| 				return;
 | |
| 			}
 | |
| 			auto tid = nana::system::this_thread_id();
 | |
| 			threadmap_[tid].timers.insert(id);
 | |
| 
 | |
| 			timer_tag & tag = holder_[id];
 | |
| 			tag.id = id;
 | |
| 			tag.tid = tid;
 | |
| 			tag.interval = interval;
 | |
| 			tag.timestamp = 0;
 | |
| 			tag.proc = proc;
 | |
| 		}
 | |
| 
 | |
| 		bool is_proc_handling() const
 | |
| 		{
 | |
| 			return is_proc_handling_;
 | |
| 		}
 | |
| 
 | |
| 		void kill(std::size_t id)
 | |
| 		{
 | |
| 			auto i = holder_.find(id);
 | |
| 			if(i != holder_.end())
 | |
| 			{
 | |
| 				auto tid = i->second.tid;
 | |
| 
 | |
| 				auto ig = threadmap_.find(tid);
 | |
| 				if(ig != threadmap_.end())	//Generally, the ig should not be the end of threadmap_
 | |
| 				{
 | |
| 					auto & group = ig->second;
 | |
| 					if(!group.proc_entered)
 | |
| 					{
 | |
| 						group.timers.erase(id);
 | |
| 						if(group.timers.empty())
 | |
| 							threadmap_.erase(ig);
 | |
| 					}
 | |
| 					else
 | |
| 						group.delay_deleted.push_back(id);
 | |
| 				}
 | |
| 				holder_.erase(i);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		bool empty() const
 | |
| 		{
 | |
| 			return (holder_.empty());
 | |
| 		}
 | |
| 
 | |
| 		void timer_proc(thread_t tid)
 | |
| 		{
 | |
| 			is_proc_handling_ = true;
 | |
| 			auto i = threadmap_.find(tid);
 | |
| 			if(i != threadmap_.end())
 | |
| 			{
 | |
| 				auto & group = i->second;
 | |
| 				group.proc_entered = true;
 | |
| 				unsigned ticks = nana::system::timestamp();
 | |
| 				for(auto timer_id : group.timers)
 | |
| 				{
 | |
| 					auto & tag = holder_[timer_id];
 | |
| 					if(tag.timestamp)
 | |
| 					{
 | |
| 						if(ticks >= tag.timestamp + tag.interval)
 | |
| 						{
 | |
| 							tag.timestamp = ticks;
 | |
| 							try
 | |
| 							{
 | |
| 								tag.proc(tag.id);
 | |
| 							}catch(...){}	//nothrow
 | |
| 						}
 | |
| 					}
 | |
| 					else
 | |
| 						tag.timestamp = ticks;
 | |
| 				}
 | |
| 				group.proc_entered = false;
 | |
| 				for(auto tmr: group.delay_deleted)
 | |
| 					group.timers.erase(tmr);
 | |
| 			}
 | |
| 			is_proc_handling_ = false;
 | |
| 		}
 | |
| 	private:
 | |
| 		bool is_proc_handling_;
 | |
| 		std::map<thread_t, timer_group> threadmap_;
 | |
| 		std::map<std::size_t, timer_tag> holder_;
 | |
| 	};
 | |
| 
 | |
| 	drawable_impl_type::drawable_impl_type()
 | |
| 	{
 | |
| 		string.tab_length = 4;
 | |
| 		string.tab_pixels = 0;
 | |
| 		string.whitespace_pixels = 0;
 | |
| #if defined(NANA_USE_XFT)
 | |
| 		conv_.handle = ::iconv_open("UTF-8", NANA_UNICODE);
 | |
| 		conv_.code = NANA_UNICODE;
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	drawable_impl_type::~drawable_impl_type()
 | |
| 	{
 | |
| #if defined(NANA_USE_XFT)
 | |
| 		::iconv_close(conv_.handle);
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	unsigned drawable_impl_type::get_color() const
 | |
| 	{
 | |
| 		return color_;
 | |
| 	}
 | |
| 
 | |
| 	unsigned drawable_impl_type::get_text_color() const
 | |
| 	{
 | |
| 		return text_color_;
 | |
| 	}
 | |
| 
 | |
| 	void drawable_impl_type::set_color(const ::nana::color& clr)
 | |
| 	{
 | |
| 		color_ = (clr.px_color().value & 0xFFFFFF);
 | |
| 	}
 | |
| 
 | |
| 	void drawable_impl_type::set_text_color(const ::nana::color& clr)
 | |
| 	{
 | |
| 		text_color_ = (clr.px_color().value & 0xFFFFFF);
 | |
| 		update_text_color();
 | |
| 	}
 | |
| 
 | |
| 	void drawable_impl_type::update_color()
 | |
| 	{
 | |
| 		if (color_ != current_color_)
 | |
| 		{
 | |
| 			auto & spec = nana::detail::platform_spec::instance();
 | |
| 			platform_scope_guard lock;
 | |
| 
 | |
| 			current_color_ = color_;
 | |
| 			auto col = color_;
 | |
| 			switch (spec.screen_depth())
 | |
| 			{
 | |
| 			case 16:
 | |
| 				col = ((((col >> 16) & 0xFF) * 31 / 255) << 11) |
 | |
| 					((((col >> 8) & 0xFF) * 63 / 255) << 5) |
 | |
| 					(col & 0xFF) * 31 / 255;
 | |
| 				break;
 | |
| 			}
 | |
| 			::XSetForeground(spec.open_display(), context, col);
 | |
| 			::XSetBackground(spec.open_display(), context, col);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void drawable_impl_type::update_text_color()
 | |
| 	{
 | |
| 		if (text_color_ != current_color_)
 | |
| 		{
 | |
| 			auto & spec = nana::detail::platform_spec::instance();
 | |
| 			platform_scope_guard lock;
 | |
| 
 | |
| 			current_color_ = text_color_;
 | |
| 			auto col = text_color_;
 | |
| 			switch (spec.screen_depth())
 | |
| 			{
 | |
| 			case 16:
 | |
| 				col = ((((col >> 16) & 0xFF) * 31 / 255) << 11) |
 | |
| 					((((col >> 8) & 0xFF) * 63 / 255) << 5) |
 | |
| 					(col & 0xFF) * 31 / 255;
 | |
| 				break;
 | |
| 			}
 | |
| 			::XSetForeground(spec.open_display(), context, col);
 | |
| 			::XSetBackground(spec.open_display(), context, col);
 | |
| 
 | |
| #if defined(NANA_USE_XFT)
 | |
| 			xft_fgcolor.color.red = ((0xFF0000 & col) >> 16) * 0x101;
 | |
| 			xft_fgcolor.color.green = ((0xFF00 & col) >> 8) * 0x101;
 | |
| 			xft_fgcolor.color.blue = (0xFF & col) * 0x101;
 | |
| 			xft_fgcolor.color.alpha = 0xFFFF;
 | |
| #endif
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	platform_scope_guard::platform_scope_guard()
 | |
| 	{
 | |
| 		platform_spec::instance().lock_xlib();
 | |
| 	}
 | |
| 
 | |
| 	platform_scope_guard::~platform_scope_guard()
 | |
| 	{
 | |
| 		platform_spec::instance().unlock_xlib();
 | |
| 	}
 | |
| 
 | |
| 	int X11_error_handler(Display*, XErrorEvent* err)
 | |
| 	{
 | |
| 	    platform_spec::instance().error_code = err->error_code;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	int X11_fatal_handler(Display*)
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	platform_spec::timer_runner_tag::timer_runner_tag()
 | |
| 		: runner(0), delete_declared(false)
 | |
| 	{}
 | |
| 
 | |
| 	platform_spec::platform_spec()
 | |
| 		:display_(0), colormap_(0), def_X11_error_handler_(0), grab_(0)
 | |
| 	{
 | |
| 		::XInitThreads();
 | |
| 		const char * langstr = getenv("LC_CTYPE");
 | |
| 		if(0 == langstr)
 | |
| 		{
 | |
| 			langstr = getenv("LC_ALL");
 | |
| 		}
 | |
| 
 | |
| 		std::string langstr_dup;
 | |
| 		if(langstr)
 | |
| 		{
 | |
| 			langstr_dup = langstr;
 | |
| 			auto dotpos = langstr_dup.find(".");
 | |
| 			if(dotpos != langstr_dup.npos)
 | |
| 			{
 | |
| 				auto beg = langstr_dup.begin() + dotpos + 1;
 | |
| 				std::transform(beg, langstr_dup.end(), beg, toupper);
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 			langstr_dup = "en_US.UTF-8";
 | |
| 		std::setlocale(LC_CTYPE, langstr_dup.c_str());
 | |
| 		if(::XSupportsLocale())
 | |
| 			::XSetLocaleModifiers(langstr_dup.c_str());
 | |
| 
 | |
| 
 | |
| 		display_ = ::XOpenDisplay(0);
 | |
| 		colormap_ = DefaultColormap(display_,  ::XDefaultScreen(display_));
 | |
| 
 | |
| 		//Initialize the member data
 | |
| 		selection_.content.utf8_string = 0;
 | |
| 		xdnd_.good_type = None;
 | |
| 
 | |
| 		atombase_.wm_protocols = ::XInternAtom(display_, "WM_PROTOCOLS", False);
 | |
| 		atombase_.wm_change_state = ::XInternAtom(display_, "WM_CHANGE_STATE", False);
 | |
| 		atombase_.wm_delete_window = ::XInternAtom(display_, "WM_DELETE_WINDOW", False);
 | |
| 		atombase_.net_frame_extents = ::XInternAtom(display_, "_NET_FRAME_EXTENTS", False);
 | |
| 		atombase_.net_wm_state = ::XInternAtom(display_, "_NET_WM_STATE", False);
 | |
| 		atombase_.net_wm_state_skip_taskbar = ::XInternAtom(display_, "_NET_WM_STATE_SKIP_TASKBAR", False);
 | |
| 		atombase_.net_wm_state_fullscreen = ::XInternAtom(display_, "_NET_WM_STATE_FULLSCREEN", False);
 | |
| 		atombase_.net_wm_state_maximized_horz = ::XInternAtom(display_, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
 | |
| 		atombase_.net_wm_state_maximized_vert = ::XInternAtom(display_, "_NET_WM_STATE_MAXIMIZED_VERT", False);
 | |
| 		atombase_.net_wm_state_modal = ::XInternAtom(display_, "_NET_WM_STATE_MODAL", False);
 | |
| 		atombase_.net_wm_name = ::XInternAtom(display_, "_NET_WM_NAME", False);
 | |
| 		atombase_.net_wm_window_type = ::XInternAtom(display_, "_NET_WM_WINDOW_TYPE", False);
 | |
| 		atombase_.net_wm_window_type_normal = ::XInternAtom(display_, "_NET_WM_WINDOW_TYPE_NORMAL", False);
 | |
| 		atombase_.net_wm_window_type_utility = ::XInternAtom(display_, "_NET_WM_WINDOW_TYPE_UTILITY", False);
 | |
| 		atombase_.net_wm_window_type_dialog = ::XInternAtom(display_, "_NET_WM_WINDOW_TYPE_DIALOG", False);
 | |
| 		atombase_.motif_wm_hints = ::XInternAtom(display_, "_MOTIF_WM_HINTS", False);
 | |
| 
 | |
| 		atombase_.clipboard = ::XInternAtom(display_, "CLIPBOARD", True);
 | |
| 		atombase_.text = ::XInternAtom(display_, "TEXT", True);
 | |
| 		atombase_.text_uri_list = ::XInternAtom(display_, "text/uri-list", True);
 | |
| 		atombase_.utf8_string = ::XInternAtom(display_, "UTF8_STRING", True);
 | |
| 		atombase_.targets = ::XInternAtom(display_, "TARGETS", True);
 | |
| 
 | |
| 		atombase_.xdnd_aware = ::XInternAtom(display_, "XdndAware", False);
 | |
| 		atombase_.xdnd_enter = ::XInternAtom(display_, "XdndEnter", False);
 | |
| 		atombase_.xdnd_position = ::XInternAtom(display_, "XdndPosition", False);
 | |
| 		atombase_.xdnd_status	= ::XInternAtom(display_, "XdndStatus", False);
 | |
| 		atombase_.xdnd_action_copy = ::XInternAtom(display_, "XdndActionCopy", False);
 | |
| 		atombase_.xdnd_drop = ::XInternAtom(display_, "XdndDrop", False);
 | |
| 		atombase_.xdnd_selection = ::XInternAtom(display_, "XdndSelection", False);
 | |
| 		atombase_.xdnd_typelist = ::XInternAtom(display_, "XdndTypeList", False);
 | |
| 		atombase_.xdnd_finished = ::XInternAtom(display_, "XdndFinished", False);
 | |
| 
 | |
| 		msg_dispatcher_ = new msg_dispatcher(display_);
 | |
| 
 | |
| 		platform_abstraction::initialize();
 | |
| 	}
 | |
| 
 | |
| 	platform_spec::~platform_spec()
 | |
| 	{
 | |
| 		delete msg_dispatcher_;
 | |
| 
 | |
| 		//The font should be destroyed before closing display,
 | |
| 		//otherwise it crashs
 | |
| 		platform_abstraction::shutdown();
 | |
| 
 | |
| 		close_display();
 | |
| 	}
 | |
| 
 | |
| 	Display* platform_spec::open_display()
 | |
| 	{
 | |
| 		return display_;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::close_display()
 | |
| 	{
 | |
| 		if(display_)
 | |
| 		{
 | |
| 		    ::XSync(reinterpret_cast<Display*>(display_), true);
 | |
| 			::XCloseDisplay(reinterpret_cast<Display*>(display_));
 | |
| 			display_ = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::lock_xlib()
 | |
| 	{
 | |
| 		xlib_locker_.lock();
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::unlock_xlib()
 | |
| 	{
 | |
| 		xlib_locker_.unlock();
 | |
| 	}
 | |
| 
 | |
| 	Window platform_spec::root_window()
 | |
| 	{
 | |
| 		return ::XDefaultRootWindow(reinterpret_cast<Display*>(display_));
 | |
| 	}
 | |
| 
 | |
| 	int platform_spec::screen_depth()
 | |
| 	{
 | |
| 		return ::XDefaultDepth(display_, ::XDefaultScreen(display_));
 | |
| 	}
 | |
| 
 | |
| 	Visual* platform_spec::screen_visual()
 | |
| 	{
 | |
| 		return ::XDefaultVisual(display_, ::XDefaultScreen(display_));
 | |
| 	}
 | |
| 
 | |
| 	Colormap& platform_spec::colormap()
 | |
| 	{
 | |
| 		return colormap_;
 | |
| 	}
 | |
| 
 | |
| 	platform_spec& platform_spec::instance()
 | |
| 	{
 | |
| 		static platform_spec object;
 | |
| 		return object;
 | |
| 	}
 | |
| 
 | |
| 	const atombase_tag& platform_spec::atombase() const
 | |
| 	{
 | |
| 		return atombase_;
 | |
| 	}
 | |
| 
 | |
| 	//There are three members make_owner(), get_owner() and remove(),
 | |
| 	//they are maintain a table to discribe the owner of windows because of the feature in X11, the
 | |
| 	//owner of top level window must be RootWindow.
 | |
| 	void platform_spec::make_owner(native_window_type owner, native_window_type wd)
 | |
| 	{
 | |
| 		platform_scope_guard lock;
 | |
| 		wincontext_[wd].owner = owner;
 | |
| 		
 | |
| 		auto& owner_ctx = wincontext_[owner];
 | |
| 		if(!owner_ctx.owned)
 | |
| 			owner_ctx.owned = new std::vector<native_window_type>;
 | |
| 		owner_ctx.owned->push_back(wd);
 | |
| 	}
 | |
| 
 | |
| 	bool platform_spec::umake_owner(native_window_type child)
 | |
| 	{
 | |
| 		platform_scope_guard lock;
 | |
| 
 | |
| 		auto i = wincontext_.find(child);
 | |
| 		if(i == wincontext_.end())
 | |
| 			return false;
 | |
| 
 | |
| 		if(i->second.owner)
 | |
| 		{
 | |
| 			auto u = wincontext_.find(i->second.owner);
 | |
| 			if(u != wincontext_.end())
 | |
| 			{
 | |
| 				auto * owned = u->second.owned;
 | |
| 				if(owned)
 | |
| 				{
 | |
| 					auto j = std::find(owned->begin(), owned->end(), child);
 | |
| 					if(j != owned->end())
 | |
| 						owned->erase(j);
 | |
| 
 | |
| 					if(owned->empty())
 | |
| 					{
 | |
| 						delete owned;
 | |
| 						u->second.owned = nullptr;
 | |
| 						//The owner owns no child. If it is not a child of other owners,
 | |
| 						//remove it.
 | |
| 						if(nullptr == u->second.owner)
 | |
| 							wincontext_.erase(u);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			i->second.owner = nullptr;
 | |
| 		}
 | |
| 
 | |
| 		//Don't remove the ownerships which the child is a owner window.
 | |
| 		if(nullptr == i->second.owned)
 | |
| 			wincontext_.erase(i);
 | |
| 
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	native_window_type platform_spec::get_owner(native_window_type wd) const
 | |
| 	{
 | |
| 		platform_scope_guard psg;
 | |
| 		auto i = wincontext_.find(wd);
 | |
| 		return (i != wincontext_.end() ? i->second.owner : nullptr);
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::remove(native_window_type wd)
 | |
| 	{
 | |
| 		msg_dispatcher_->erase(reinterpret_cast<Window>(wd));
 | |
| 
 | |
| 		platform_scope_guard lock;
 | |
| 		if(umake_owner(wd))
 | |
| 		{
 | |
| 			auto & wd_manager = detail::bedrock::instance().wd_manager();
 | |
| 
 | |
| 			std::vector<native_window_type> owned_children;
 | |
| 
 | |
| 			auto i = wincontext_.find(wd);
 | |
| 			if(i != wincontext_.end())
 | |
| 			{
 | |
| 				if(i->second.owned)
 | |
| 				{
 | |
| 					for(auto child : *i->second.owned)
 | |
| 						owned_children.push_back(child);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			//Closing a child will erase the wd from the table wincontext_, so the 
 | |
| 			//iterator i can't be reused after children closed.
 | |
| 			set_error_handler();
 | |
| 			for(auto u = owned_children.rbegin(); u != owned_children.rend(); ++u)
 | |
| 				wd_manager.close(wd_manager.root(*u));
 | |
| 			rev_error_handler();
 | |
| 
 | |
| 			i = wincontext_.find(wd);
 | |
| 			if(i != wincontext_.end())
 | |
| 			{
 | |
| 				delete i->second.owned;
 | |
| 				wincontext_.erase(i);
 | |
| 			}
 | |
| 		}
 | |
| 		iconbase_.erase(wd);
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	void platform_spec::write_keystate(const XKeyEvent& xkey)
 | |
| 	{
 | |
| 		this->key_state_ = xkey;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::read_keystate(XKeyEvent& xkey)
 | |
| 	{
 | |
| 		xkey = this->key_state_;
 | |
| 	}
 | |
| 
 | |
| 	XIC platform_spec::caret_input_context(native_window_type wd) const
 | |
| 	{
 | |
| 		platform_scope_guard psg;
 | |
| 		auto i = caret_holder_.carets.find(wd);
 | |
| 		if(i != caret_holder_.carets.end())
 | |
| 			return i->second->input_context;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::caret_open(native_window_type wd, const ::nana::size& caret_sz)
 | |
| 	{
 | |
| 		bool is_start_routine = false;
 | |
| 		platform_scope_guard psg;
 | |
| 		auto & addr = caret_holder_.carets[wd];
 | |
| 		if(nullptr == addr)
 | |
| 		{
 | |
| 			::XSetLocaleModifiers("");
 | |
| 
 | |
| 			addr = new caret_rep(wd);
 | |
| 			is_start_routine = (caret_holder_.carets.size() == 1);
 | |
| 			addr->input_method = ::XOpenIM(display_, 0, 0, 0);
 | |
| 			if(addr->input_method)
 | |
| 			{
 | |
| 				XIMStyles* imstyle;
 | |
| 				::XGetIMValues(addr->input_method, XNQueryInputStyle, &imstyle, nullptr, nullptr);
 | |
| 				if(imstyle)
 | |
| 				{
 | |
| 					if(imstyle->count_styles)
 | |
| 					{
 | |
| 						addr->input_font = 0;
 | |
| 						XVaNestedList preedit_attr = ::XVaCreateNestedList(0, XNSpotLocation, &(addr->input_spot), nullptr);
 | |
| 						XVaNestedList status_attr = ::XVaCreateNestedList(0, XNAreaNeeded, &(addr->input_status_area), nullptr);
 | |
| 						XIMStyle * style_end = imstyle->supported_styles + imstyle->count_styles;
 | |
| 						bool has_status = false;
 | |
| 						bool has_preedit = false;
 | |
| 						for(XIMStyle * i = imstyle->supported_styles; i != style_end; ++i)
 | |
| 						{
 | |
| 							if(*i == (XIMPreeditPosition | XIMStatusArea))
 | |
| 							{
 | |
| 								has_status = has_preedit = true;
 | |
| 								break;
 | |
| 							}
 | |
| 							else if(*i == (XIMPreeditPosition | XIMStatusNothing))
 | |
| 								has_preedit = true;
 | |
| 						}
 | |
| 
 | |
| 						if(has_status)
 | |
| 						{
 | |
| 							addr->input_context = ::XCreateIC(addr->input_method, XNInputStyle, (XIMPreeditPosition | XIMStatusArea),
 | |
| 														XNPreeditAttributes, preedit_attr, XNStatusAttributes, status_attr,
 | |
| 														XNClientWindow, reinterpret_cast<Window>(wd), nullptr);
 | |
| 						}
 | |
| 						else
 | |
| 							addr->input_context = 0;
 | |
| 
 | |
| 						if((addr->input_context == 0) && has_preedit)
 | |
| 						{
 | |
| 							addr->input_context = ::XCreateIC(addr->input_method, XNInputStyle, (XIMPreeditPosition | XIMStatusNothing),
 | |
| 															XNPreeditAttributes, preedit_attr, XNClientWindow, reinterpret_cast<Window>(wd), nullptr);
 | |
| 						}
 | |
| 
 | |
| 						if(addr->input_context)
 | |
| 						{
 | |
| 							XVaNestedList attr = ::XVaCreateNestedList(0, XNAreaNeeded, &(addr->input_status_area), XNClientWindow, reinterpret_cast<Window>(wd), nullptr);
 | |
| 							::XGetICValues(addr->input_context, XNStatusAttributes, attr, nullptr);
 | |
| 							::XFree(attr);
 | |
| 						}
 | |
| 						else
 | |
| 							addr->input_context = ::XCreateIC(addr->input_method,
 | |
| 											XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
 | |
| 											XNClientWindow, reinterpret_cast<Window>(wd),
 | |
| 											XNFocusWindow, reinterpret_cast<Window>(wd), nullptr);
 | |
| 
 | |
| 						if(addr->input_context)
 | |
| 						{
 | |
| 							//Make the IM event filter.
 | |
| 							::XGetICValues(addr->input_context, XNFilterEvents, &(addr->input_context_event_mask), nullptr);
 | |
| 							XWindowAttributes attr;
 | |
| 							::XGetWindowAttributes(display_, reinterpret_cast<Window>(wd), &attr);
 | |
| 							XSetWindowAttributes new_attr;
 | |
| 							new_attr.event_mask = (attr.your_event_mask | addr->input_context_event_mask);
 | |
| 							::XChangeWindowAttributes(display_, reinterpret_cast<Window>(wd), CWEventMask, &new_attr);
 | |
| 
 | |
| 							::XSetICValues(addr->input_context, XNResetState, XIMPreserveState, nullptr);
 | |
| 						}
 | |
| 						::XFree(preedit_attr);
 | |
| 						::XFree(status_attr);
 | |
| 					}
 | |
| 					::XFree(imstyle);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		addr->visible = false;
 | |
| 		addr->rev_graph.make(caret_sz);
 | |
| 		addr->size = caret_sz;
 | |
| 
 | |
| 		if(addr->input_context && (false == addr->has_input_method_focus))
 | |
| 		{
 | |
| 			::XSetICFocus(addr->input_context);
 | |
| 			addr->has_input_method_focus = true;
 | |
| 		}
 | |
| 
 | |
| 		if(is_start_routine)
 | |
| 		{
 | |
| 			caret_holder_.exit_thread = false;
 | |
| 			auto fn = [this](){ this->_m_caret_routine(); };
 | |
| 			caret_holder_.thr.reset(new std::thread(fn));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::caret_close(native_window_type wd)
 | |
| 	{
 | |
| 		bool is_end_routine = false;
 | |
| 		{
 | |
| 			platform_scope_guard psg;
 | |
| 
 | |
| 			auto i = caret_holder_.carets.find(wd);
 | |
| 			if(i != caret_holder_.carets.end())
 | |
| 			{
 | |
| 				auto addr = i->second;
 | |
| 				if(addr->input_context)
 | |
| 				{
 | |
| 					if(addr->has_input_method_focus)
 | |
| 					{
 | |
| 						::XUnsetICFocus(addr->input_context);
 | |
| 						addr->has_input_method_focus = false;
 | |
| 					}
 | |
| 
 | |
| 					//Remove the IM event filter.
 | |
| 					set_error_handler();
 | |
| 					XWindowAttributes attr;
 | |
| 					if(BadWindow != ::XGetWindowAttributes(display_, reinterpret_cast<Window>(wd), &attr))
 | |
| 					{
 | |
| 						if((attr.your_event_mask & addr->input_context_event_mask) == addr->input_context_event_mask)
 | |
| 						{
 | |
| 							XSetWindowAttributes new_attr;
 | |
| 
 | |
| 							//Don't remove the KeyPress and KeyRelease mask(0x3), otherwise the window will not receive
 | |
| 							//Keyboard events after destroying caret
 | |
| 							new_attr.event_mask = (attr.your_event_mask & ~(addr->input_context_event_mask & (~0x3)));
 | |
| 							::XChangeWindowAttributes(display_, reinterpret_cast<Window>(wd), CWEventMask, &new_attr);
 | |
| 						}
 | |
| 					}
 | |
| 					rev_error_handler();
 | |
| 
 | |
| 					::XDestroyIC(addr->input_context);
 | |
| 				}
 | |
| 
 | |
| 				if(addr->input_font)
 | |
| 					::XFreeFontSet(display_, addr->input_font);
 | |
| 
 | |
| 				if(addr->input_method)
 | |
| 					::XCloseIM(addr->input_method);
 | |
| 
 | |
| 				delete i->second;
 | |
| 				caret_holder_.carets.erase(i);
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			is_end_routine = (caret_holder_.carets.size() == 0);
 | |
| 		}
 | |
| 
 | |
| 		if(is_end_routine && (caret_holder_.thr != nullptr) && (caret_holder_.thr->joinable()))
 | |
| 		{
 | |
| 			caret_holder_.exit_thread = true;
 | |
| 			caret_holder_.thr->join();
 | |
| 			caret_holder_.thr.reset();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::caret_pos(native_window_type wd, const point& pos)
 | |
| 	{
 | |
| 		platform_scope_guard psg;
 | |
| 		auto i = caret_holder_.carets.find(wd);
 | |
| 		if(i != caret_holder_.carets.end())
 | |
| 		{
 | |
| 			i->second->reinstate();
 | |
| 			i->second->pos = pos;
 | |
| 		}
 | |
| 		auto addr = i->second;
 | |
| 		if(addr && addr->input_context) {
 | |
| 			XPoint spot;
 | |
| 			XVaNestedList list;
 | |
| 			spot.x = pos.x;
 | |
| 			spot.y = pos.y + addr->size.height;
 | |
| 			list = ::XVaCreateNestedList(0, XNSpotLocation, &spot,
 | |
| 					XNForeground, 0,
 | |
| 					XNBackground, 0,
 | |
| 					(void *)0);
 | |
| 			::XSetICValues(addr->input_context, XNPreeditAttributes, list, NULL);
 | |
| 			::XFree(list);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::caret_visible(native_window_type wd, bool vis)
 | |
| 	{
 | |
| 		platform_scope_guard psg;
 | |
| 		auto i = caret_holder_.carets.find(wd);
 | |
| 		if(i != caret_holder_.carets.end())
 | |
| 		{
 | |
| 			auto & crt = *i->second;
 | |
| 			if(crt.visible != vis)
 | |
| 			{
 | |
| 				if(vis == false)
 | |
| 				{
 | |
| 					crt.reinstate();
 | |
| 					if(crt.input_context && crt.has_input_method_focus)
 | |
| 					{
 | |
| 						::XUnsetICFocus(crt.input_context);
 | |
| 						crt.has_input_method_focus = false;
 | |
| 					}
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					if(crt.input_context && (false == crt.has_input_method_focus))
 | |
| 					{
 | |
| 						::XSetICFocus(crt.input_context);
 | |
| 						crt.has_input_method_focus = true;
 | |
| 					}
 | |
| 				}
 | |
| 				crt.visible = vis;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	bool platform_spec::caret_update(native_window_type wd, nana::paint::graphics& /*root_graph*/, bool after_mapping)
 | |
| 	{
 | |
| 		platform_scope_guard psg;
 | |
| 		auto i = caret_holder_.carets.find(wd);
 | |
| 		if(i != caret_holder_.carets.end())
 | |
| 		{
 | |
| 			auto & crt = *i->second;
 | |
| 			if(!after_mapping)
 | |
| 			{
 | |
| 				return crt.reinstate();
 | |
| 			}
 | |
| 			else
 | |
| 				crt.twinkle();
 | |
| 		}
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::set_error_handler()
 | |
| 	{
 | |
| 		platform_scope_guard psg;
 | |
| 		error_code = 0;
 | |
| 		def_X11_error_handler_ = ::XSetErrorHandler(X11_error_handler);
 | |
| 	}
 | |
| 
 | |
| 	int platform_spec::rev_error_handler()
 | |
| 	{
 | |
| 		if(def_X11_error_handler_)
 | |
| 		{
 | |
| 			platform_scope_guard psg;
 | |
| 			::XSync(display_, False);
 | |
| 			::XSetErrorHandler(def_X11_error_handler_);
 | |
| 		}
 | |
| 		return error_code;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::_m_caret_routine()
 | |
| 	{
 | |
| 		while(false == caret_holder_.exit_thread)
 | |
| 		{
 | |
| 			if(xlib_locker_.try_lock())
 | |
| 			{
 | |
| 				for(auto i : caret_holder_.carets)
 | |
| 					i.second->twinkle();
 | |
| 
 | |
| 				xlib_locker_.unlock();
 | |
| 			}
 | |
| 			for(int i = 0; i < 5 && (false == caret_holder_.exit_thread); ++i)
 | |
| 				nana::system::sleep(100);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	Window platform_spec::grab(Window wd)
 | |
| 	{
 | |
| 		Window r = grab_;
 | |
| 		grab_ = wd;
 | |
| 		return r;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::set_timer(std::size_t id, std::size_t interval, void (*timer_proc)(std::size_t))
 | |
| 	{
 | |
| 		std::lock_guard<decltype(timer_.mutex)> lock(timer_.mutex);
 | |
| 		if(0 == timer_.runner)
 | |
| 			timer_.runner = new timer_runner;
 | |
| 		timer_.runner->set(id, interval, timer_proc);
 | |
| 		timer_.delete_declared = false;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::kill_timer(std::size_t id)
 | |
| 	{
 | |
| 		if(timer_.runner == 0) return;
 | |
| 
 | |
| 		std::lock_guard<decltype(timer_.mutex)> lock(timer_.mutex);
 | |
| 		timer_.runner->kill(id);
 | |
| 		if(timer_.runner->empty())
 | |
| 		{
 | |
| 			if(timer_.runner->is_proc_handling() == false)
 | |
| 			{
 | |
| 				delete timer_.runner;
 | |
| 				timer_.runner = 0;
 | |
| 			}
 | |
| 			else
 | |
| 				timer_.delete_declared = true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::timer_proc(thread_t tid)
 | |
| 	{
 | |
| 		std::lock_guard<decltype(timer_.mutex)> lock(timer_.mutex);
 | |
| 		if(timer_.runner)
 | |
| 		{
 | |
| 			timer_.runner->timer_proc(tid);
 | |
| 			if(timer_.delete_declared)
 | |
| 			{
 | |
| 				delete timer_.runner;
 | |
| 				timer_.runner = 0;
 | |
| 				timer_.delete_declared = false;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::msg_insert(native_window_type wd)
 | |
| 	{
 | |
| 		msg_dispatcher_->insert(reinterpret_cast<Window>(wd));
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::msg_set(timer_proc_type tp, event_proc_type ep)
 | |
| 	{
 | |
| 		msg_dispatcher_->set(tp, ep, &platform_spec::_m_msg_filter);
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::msg_dispatch(native_window_type modal)
 | |
| 	{
 | |
| 		msg_dispatcher_->dispatch(reinterpret_cast<Window>(modal));
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::msg_dispatch(std::function<bool(const msg_packet_tag&)> 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)
 | |
| 		{
 | |
| 			Atom clipboard = atombase_.clipboard;
 | |
| 			xlib_locker_.lock();
 | |
| 			Window owner = ::XGetSelectionOwner(display_, clipboard);
 | |
| 			if(owner)
 | |
| 			{
 | |
| 				selection_tag::item_t * selim = new selection_tag::item_t;
 | |
| 				selim->type = type;
 | |
| 				selim->requestor = reinterpret_cast<Window>(requestor);
 | |
| 				selim->buffer = nullptr;
 | |
| 				selim->bufsize = 0;
 | |
| 
 | |
| 				this->selection_.items.push_back(selim);
 | |
| 				::XConvertSelection(display_, clipboard, type, clipboard,
 | |
| 							reinterpret_cast<Window>(requestor), CurrentTime);
 | |
| 				::XFlush(display_);
 | |
| 				xlib_locker_.unlock();
 | |
| 
 | |
| 				std::unique_lock<decltype(selim->cond_mutex)> lock(selim->cond_mutex);
 | |
| 				selim->cond.wait(lock);
 | |
| 
 | |
| 				size = selim->bufsize;
 | |
| 				void * retbuf = selim->buffer;
 | |
| 				delete selim;
 | |
| 				return retbuf;
 | |
| 			}
 | |
| 			else
 | |
| 				xlib_locker_.unlock();
 | |
| 		}
 | |
| 		return nullptr;
 | |
| 	}
 | |
| 
 | |
| 	void platform_spec::write_selection(native_window_type owner, Atom type, const void * buf, size_t bufsize)
 | |
| 	{
 | |
| 		platform_scope_guard psg;
 | |
| 		::XSetSelectionOwner(display_, XA_PRIMARY, reinterpret_cast<Window>(owner), CurrentTime);
 | |
| 		::XSetSelectionOwner(display_, atombase_.clipboard, reinterpret_cast<Window>(owner), CurrentTime);
 | |
| 		::XFlush(display_);
 | |
| 		if(XA_STRING == type || atombase_.utf8_string == type)
 | |
| 		{
 | |
| 			std::string * utf8str = selection_.content.utf8_string;
 | |
| 			if(utf8str)
 | |
| 				utf8str->clear();
 | |
| 			else
 | |
| 				utf8str = new std::string;
 | |
| 
 | |
| 			utf8str->append(reinterpret_cast<const char*>(buf), reinterpret_cast<const char*>(buf) + bufsize);
 | |
| 			selection_.content.utf8_string = utf8str;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	//Icon Storage
 | |
| 	const nana::paint::graphics& platform_spec::keep_window_icon(native_window_type wd, const nana::paint::image& img)
 | |
| 	{
 | |
| 		nana::paint::graphics & graph = iconbase_[wd];
 | |
| 		graph.make(img.size());
 | |
| 		img.paste(graph, {});
 | |
| 		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<Window>(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8,
 | |
| 			PropModeReplace, reinterpret_cast<unsigned char*>(&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
 | |
| 	//		1 = msg_dispatcher dispatches the msg_packet_tag that modified by _m_msg_filter
 | |
| 	//		2 = msg_dispatcher should ignore the msg, because the XEvent is processed by _m_msg_filter
 | |
| 	int platform_spec::_m_msg_filter(XEvent& evt, msg_packet_tag& msg)
 | |
| 	{
 | |
| 		auto & bedrock = detail::bedrock::instance();
 | |
| 
 | |
| 		platform_spec & self = instance();
 | |
| 		if(KeyPress == evt.type || KeyRelease == evt.type)
 | |
| 		{
 | |
| 			auto menu_wd = bedrock.get_menu(reinterpret_cast<native_window_type>(evt.xkey.window), true);
 | |
| 			if(menu_wd)
 | |
| 				evt.xkey.window = reinterpret_cast<Window>(menu_wd);
 | |
| 		}
 | |
| 		else if(SelectionNotify == evt.type)
 | |
| 		{
 | |
| 			if(evt.xselection.property)
 | |
| 			{
 | |
| 				Atom type;
 | |
| 				int format;
 | |
| 				unsigned long len, bytes_left = 0;
 | |
| 				unsigned char *data;
 | |
| 
 | |
| 				::XGetWindowProperty(self.display_, evt.xselection.requestor, evt.xselection.property, 0, 0, 0,
 | |
| 									 AnyPropertyType, &type, &format, &len, &bytes_left, &data);
 | |
| 
 | |
| 				if(evt.xselection.property == self.atombase_.clipboard)
 | |
| 				{
 | |
| 					platform_scope_guard psg;
 | |
| 
 | |
| 					if(self.selection_.items.size())
 | |
| 					{
 | |
| 						selection_tag::item_t * im = self.selection_.items.front();
 | |
| 
 | |
| 						if(bytes_left > 0 && (type == im->type))
 | |
| 						{
 | |
| 							unsigned long dummy_bytes_left;
 | |
| 							if(Success == ::XGetWindowProperty(self.display_, evt.xselection.requestor,
 | |
| 															evt.xselection.property, 0, bytes_left,
 | |
| 															0, AnyPropertyType, &type, &format, &len,
 | |
| 															&dummy_bytes_left, &data))
 | |
| 							{
 | |
| 								im->buffer = data;
 | |
| 								im->bufsize = len;
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 						self.selection_.items.erase(self.selection_.items.begin());
 | |
| 
 | |
| 						std::lock_guard<decltype(im->cond_mutex)> lock(im->cond_mutex);
 | |
| 						im->cond.notify_one();
 | |
| 
 | |
| 					}
 | |
| 				}
 | |
| 				else if(evt.xselection.property == self.atombase_.xdnd_selection)
 | |
| 				{
 | |
| 					bool accepted = false;
 | |
| 					msg.kind = msg_packet_tag::pkt_family::mouse_drop;
 | |
| 					msg.u.mouse_drop.window = 0;
 | |
| 					if(bytes_left > 0 && type == self.xdnd_.good_type)
 | |
| 					{
 | |
| 						unsigned long dummy_bytes_left;
 | |
| 						if(Success == ::XGetWindowProperty(self.display_, evt.xselection.requestor,
 | |
| 															evt.xselection.property, 0, bytes_left,
 | |
| 															0, AnyPropertyType, &type, &format, &len,
 | |
| 															&dummy_bytes_left, &data))
 | |
| 						{
 | |
| 							auto files = new std::vector<std::string>;
 | |
| 							std::stringstream ss(reinterpret_cast<char*>(data));
 | |
| 							while(true)
 | |
| 							{
 | |
| 								std::string file;
 | |
| 								std::getline(ss, file);
 | |
| 								if(false == ss.good()) break;
 | |
| 								if(0 == file.find("file://"))
 | |
| 									file = file.substr(7);
 | |
| 
 | |
| 								while(file.size())
 | |
| 								{
 | |
| 									auto ch = file.back();
 | |
| 									if('\r' == ch || '\n' == ch)
 | |
| 										file.pop_back();
 | |
| 									else
 | |
| 										break;
 | |
| 								}
 | |
| 
 | |
| 								files->push_back(file);
 | |
| 							}
 | |
| 							if(files->size())
 | |
| 							{
 | |
| 								msg.u.mouse_drop.window = evt.xselection.requestor;
 | |
| 								msg.u.mouse_drop.x = self.xdnd_.pos.x;
 | |
| 								msg.u.mouse_drop.y = self.xdnd_.pos.y;
 | |
| 								msg.u.mouse_drop.files = files;
 | |
| 							}
 | |
| 							else
 | |
| 								delete files;
 | |
| 
 | |
| 							accepted = true;
 | |
| 							::XFree(data);
 | |
| 						}
 | |
| 					}
 | |
| 					XEvent respond;
 | |
| 					::memset(respond.xclient.data.l, 0, sizeof(respond.xclient.data.l));
 | |
| 					respond.xclient.display = self.display_;
 | |
| 					respond.xclient.window = self.xdnd_.wd_src;
 | |
| 					respond.xclient.message_type = self.atombase_.xdnd_finished;
 | |
| 					respond.xclient.format = 32;
 | |
| 					respond.xclient.data.l[0] = evt.xselection.requestor;
 | |
| 					if(accepted)
 | |
| 					{
 | |
| 						respond.xclient.data.l[1] = 1;
 | |
| 						respond.xclient.data.l[2] = self.atombase_.xdnd_action_copy;
 | |
| 					}
 | |
| 					::XSendEvent(self.display_, self.xdnd_.wd_src, False, NoEventMask, &respond);
 | |
| 					::XFlush(self.display_);
 | |
| 
 | |
| 					if(msg.u.mouse_drop.window)
 | |
| 						return 1;	//Use the packet directly.
 | |
| 				}
 | |
| 			}
 | |
| 			::XFlush(self.display_);
 | |
| 			return 2;
 | |
| 		}
 | |
| 		else if(SelectionRequest == evt.type)
 | |
| 		{
 | |
| 			auto disp = evt.xselectionrequest.display;
 | |
| 			XEvent respond;
 | |
| 
 | |
| 			respond.xselection.property = evt.xselectionrequest.property;
 | |
| 			if(self.atombase_.targets == evt.xselectionrequest.target)
 | |
| 			{
 | |
| 				std::vector<Atom> atoms;
 | |
| 				if(self.selection_.content.utf8_string)
 | |
| 				{
 | |
| 					atoms.push_back(self.atombase_.utf8_string);
 | |
| 					atoms.push_back(XA_STRING);
 | |
| 				}
 | |
| 
 | |
| 				::XChangeProperty(self.display_, evt.xselectionrequest.requestor,
 | |
| 						evt.xselectionrequest.property, XA_ATOM, 32, 0,
 | |
| 						reinterpret_cast<unsigned char*>(atoms.size() ? &atoms[0] : 0), static_cast<int>(atoms.size()));
 | |
| 			}
 | |
| 			else if(XA_STRING == evt.xselectionrequest.target || self.atombase_.utf8_string == evt.xselectionrequest.target)
 | |
| 			{
 | |
| 				std::string str;
 | |
| 				if(self.selection_.content.utf8_string)
 | |
| 					str = *self.selection_.content.utf8_string;
 | |
| 
 | |
| 				::XChangeProperty(self.display_, evt.xselectionrequest.requestor, evt.xselectionrequest.property, evt.xselectionrequest.target, 8, 0,
 | |
| 									reinterpret_cast<unsigned char*>(str.size() ? const_cast<std::string::value_type*>(str.c_str()) : 0), static_cast<int>(str.size()));
 | |
| 			}
 | |
| 			else
 | |
| 				respond.xselection.property = None;
 | |
| 
 | |
| 			respond.xselection.type = SelectionNotify;
 | |
| 			respond.xselection.display = disp;
 | |
| 			respond.xselection.requestor = evt.xselectionrequest.requestor;
 | |
| 			respond.xselection.selection = evt.xselectionrequest.selection;
 | |
| 			respond.xselection.target = evt.xselectionrequest.target;
 | |
| 			respond.xselection.time = evt.xselectionrequest.time;
 | |
| 
 | |
| 			platform_scope_guard psg;
 | |
| 			::XSendEvent(disp, evt.xselectionrequest.requestor, 0, 0, &respond);
 | |
| 			::XFlush(disp);
 | |
| 			return 2;
 | |
| 		}
 | |
| 		else if(ClientMessage == evt.type)
 | |
| 		{
 | |
| 			if(self.atombase_.xdnd_enter == evt.xclient.message_type)
 | |
| 			{
 | |
| 				const Atom * atoms = reinterpret_cast<const Atom*>(&(evt.xclient.data.l[2]));
 | |
| 				unsigned long len = 3;
 | |
| 				unsigned char * data = 0;
 | |
| 				self.xdnd_.wd_src = evt.xclient.data.l[0];
 | |
| 
 | |
| 				//Check whether there is more than three types.
 | |
| 				if(evt.xclient.data.l[1] & 1)
 | |
| 				{
 | |
| 					Atom type;
 | |
| 					int format;
 | |
| 					unsigned long bytes_left;
 | |
| 					::XGetWindowProperty(self.display_, self.xdnd_.wd_src, self.atombase_.xdnd_typelist, 0, 0, False,
 | |
| 										XA_ATOM, &type, &format, &len, &bytes_left, &data);
 | |
| 
 | |
| 					if(bytes_left > 0)
 | |
| 					{
 | |
| 						::XGetWindowProperty(self.display_, self.xdnd_.wd_src, self.atombase_.xdnd_typelist,
 | |
| 											0, bytes_left, False, XA_ATOM,
 | |
| 											&type, &format, &len, &bytes_left, &data);
 | |
| 						if(XA_ATOM == type && len > 0)
 | |
| 							atoms = reinterpret_cast<const Atom*>(data);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				self.xdnd_.good_type = None;
 | |
| 				for(unsigned long i = 0; i < len; ++i)
 | |
| 				{
 | |
| 					if(atoms[i] == self.atombase_.text_uri_list)
 | |
| 					{
 | |
| 						self.xdnd_.good_type = self.atombase_.text_uri_list;
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if(data)
 | |
| 					::XFree(data);
 | |
| 
 | |
| 				return 2;
 | |
| 			}
 | |
| 			else if(self.atombase_.xdnd_position == evt.xclient.message_type)
 | |
| 			{
 | |
| 				Window wd_src = evt.xclient.data.l[0];
 | |
| 				int x = (evt.xclient.data.l[2] >> 16);
 | |
| 				int y = (evt.xclient.data.l[2] & 0xFFFF);
 | |
| 
 | |
| 				bool accepted = false;
 | |
| 				//We have got the type what we want.
 | |
| 				if(self.xdnd_.good_type != None)
 | |
| 				{
 | |
| 					Window child;
 | |
| 					::XTranslateCoordinates(self.display_, self.root_window(), evt.xclient.window, x, y, &self.xdnd_.pos.x, &self.xdnd_.pos.y, &child);
 | |
| 
 | |
| 					auto wd = bedrock.wd_manager().find_window(reinterpret_cast<native_window_type>(evt.xclient.window), self.xdnd_.pos);
 | |
| 					if(wd && wd->flags.dropable)
 | |
| 					{
 | |
| 						accepted = true;
 | |
| 						self.xdnd_.timestamp = evt.xclient.data.l[3];
 | |
| 						self.xdnd_.pos = wd->pos_root;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				XEvent respond;
 | |
| 				memset(&respond, 0, sizeof respond);
 | |
| 				respond.xany.type = ClientMessage;
 | |
| 				respond.xany.display = self.display_;
 | |
| 				respond.xclient.window = wd_src;
 | |
| 				respond.xclient.message_type = self.atombase_.xdnd_status;
 | |
| 				respond.xclient.format = 32;
 | |
| 
 | |
| 				//Target window
 | |
| 				respond.xclient.data.l[0] = evt.xclient.window;
 | |
| 				//Accept set
 | |
| 				respond.xclient.data.l[1] = (accepted ? 1 : 0);
 | |
| 				respond.xclient.data.l[2] = 0;
 | |
| 				respond.xclient.data.l[3] = 0;
 | |
| 				respond.xclient.data.l[4] = self.atombase_.xdnd_action_copy;
 | |
| 
 | |
| 				::XSendEvent(self.display_, wd_src, True, NoEventMask, &respond);
 | |
| 				return 2;
 | |
| 			}
 | |
| 			else if(self.atombase_.xdnd_drop == evt.xclient.message_type)
 | |
| 			{
 | |
| 				::XConvertSelection(self.display_, self.atombase_.xdnd_selection, self.xdnd_.good_type, self.atombase_.xdnd_selection,
 | |
| 									evt.xclient.window, self.xdnd_.timestamp);
 | |
| 				//The XdndDrop should send a XdndFinished to source window.
 | |
| 				//This operation is implemented in SelectionNotify, because
 | |
| 				//XdndFinished should be sent after retrieving data.
 | |
| 				return 2;
 | |
| 			}
 | |
| 		}
 | |
| 		return 0;
 | |
| 	}
 | |
| }//end namespace detail
 | |
| }//end namespace nana
 | |
| 
 | |
| #include <nana/pop_ignore_diagnostic>
 | |
| #endif //NANA_POSIX && NANA_X11
 | 
