diff --git a/.gitignore b/.gitignore index a8f12de9..1093c749 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ _ReSharper*/ *.suo *.sdf lib/ +*.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 242a1ea2..6367ceeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,11 +19,14 @@ if(BIICODE) if(WIN32) file(GLOB_RECURSE platform_files "*/detail/win32/*") list(APPEND BII_LIB_SRC ${platform_files}) + elseif(APPLE) + file(GLOB_RECURSE platform_files "*/detail/macos_X11/*") + list(APPEND BII_LIB_SRC ${platform_files}) elseif(UNIX) file(GLOB_RECURSE platform_files "*/detail/linux_X11/*") list(APPEND BII_LIB_SRC ${platform_files}) else() - message(FATAL_ERROR "Only Windows and Unix are supported for the moment") + message(FATAL_ERROR "Only Windows and Unix are supported for the moment (Mac OS is experimental)") endif() # set compile flags @@ -61,7 +64,13 @@ if(WIN32) endif() endif() endif() -if(UNIX) +if(APPLE) + add_definitions(-DNANA_MACOS) + add_definitions(-DNANA_X11) + add_definitions(-DPLATFORM_SPEC_HPP=) + add_definitions(-DSTD_CODECVT_NOT_SUPPORTED) + include_directories(/opt/X11/include/) +elseif(UNIX) add_definitions(-DNANA_LINUX) add_definitions(-DNANA_X11) add_definitions(-DPLATFORM_SPEC_HPP=) @@ -133,8 +142,10 @@ include_directories(${NANA_INCLUDE_DIR}) aux_source_directory(${NANA_SOURCE_DIR} NANA_SOURCE) aux_source_directory(${NANA_SOURCE_DIR}/detail NANA_DETAIL_SOURCE) aux_source_directory(${NANA_SOURCE_DIR}/filesystem NANA_FILESYSTEM_SOURCE) +if(NOT APPLE) aux_source_directory(${NANA_SOURCE_DIR}/audio NANA_AUDIO_SOURCE) aux_source_directory(${NANA_SOURCE_DIR}/audio/detail NANA_AUDIO_DETAIL_SOURCE) +endif() aux_source_directory(${NANA_SOURCE_DIR}/gui NANA_GUI_SOURCE) aux_source_directory(${NANA_SOURCE_DIR}/gui/detail NANA_GUI_DETAIL_SOURCE) aux_source_directory(${NANA_SOURCE_DIR}/gui/widgets NANA_GUI_WIDGETS_SOURCE) @@ -147,8 +158,10 @@ aux_source_directory(${NANA_SOURCE_DIR}/threads NANA_THREADS_SOURCE) add_library(${PROJECT_NAME} ${NANA_SOURCE} ${NANA_DETAIL_SOURCE} ${NANA_FILESYSTEM_SOURCE} +#if(NOT APPLE) ${NANA_AUDIO_SOURCE} ${NANA_AUDIO_DETAIL_SOURCE} +#endif ${NANA_GUI_SOURCE} ${NANA_GUI_DETAIL_SOURCE} ${NANA_GUI_WIDGETS_SOURCE} @@ -156,10 +169,19 @@ add_library(${PROJECT_NAME} ${NANA_SOURCE} ${NANA_PAINT_SOURCE} ${NANA_PAINT_DETAIL_SOURCE} ${NANA_SYSTEM_SOURCE} - ${NANA_THREADS_SOURCE}) + ${NANA_THREADS_SOURCE}) + +#if(APPLE) +target_link_libraries(${PROJECT_NAME} -L/opt/X11/lib/ -lX11 -lXft -lpng -liconv) +#endif() + install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) set_property( TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14 ) + +add_executable(nana_test test.cpp) +set_property( TARGET nana_test PROPERTY CXX_STANDARD 14 ) +target_link_libraries(nana_test ${PROJECT_NAME}) diff --git a/include/nana/config.hpp b/include/nana/config.hpp index 6d2cf836..08527a55 100644 --- a/include/nana/config.hpp +++ b/include/nana/config.hpp @@ -1,7 +1,7 @@ /* * Nana Configuration * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -13,78 +13,6 @@ #ifndef NANA_CONFIG_HPP #define NANA_CONFIG_HPP - -#if defined(_MSC_VER) - #define _SCL_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_DEPRECATE - #pragma warning(disable : 4996) - - #if (_MSC_VER < 1900) - // is this a good idea? - #define NOT_IMPLEMENTED_KEYWORD_noexcept - #endif // _MSC_VER < 1900 - #if (_MSC_VER == 1900) - // google: break any code that tries to use codecvt or codecvt. - // google: It appears the C++ libs haven't been compiled with native char16_t/char32_t support. - // google: Those definitions are for codecvt::id, codecvt::id and codecvt::id respectively. - // However, the codecvt::id and codecvt::id definitions aren't there, and indeed, if you look at locale0.cpp in the CRT source code you'll see they're not defined at all. - // google: That's a known issue, tracked by an active bug (DevDiv#1060849). We were able to update the STL's headers in response to char16_t/char32_t, but we still need to update the separately compiled sources. - #define STD_CODECVT_NOT_SUPPORTED - #endif // _MSC_VER == 1900 -#endif // _MSVC - -//Select platform automatically -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -//Windows: - #define NANA_WINDOWS 1 - #define PLATFORM_SPEC_HPP - - //Test if it is MINGW - #if defined(__MINGW32__) || defined(__MINGW64__) - #define NANA_MINGW - #define STD_CODECVT_NOT_SUPPORTED - #if (__GNUC__ == 4) && ((__GNUC_MINOR__ < 8) || (__GNUC_MINOR__ == 8 && __GNUC_PATCHLEVEL__ < 1)) - //Use this flag if MinGW version is older than 4.8.1 - #define STD_THREAD_NOT_SUPPORTED - #endif - #endif -#elif (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) -//Linux: - #define NANA_LINUX 1 - #define NANA_X11 1 - #define PLATFORM_SPEC_HPP - #define STD_CODECVT_NOT_SUPPORTED -#else -# static_assert(false, "Only Windows and Unix are supported now"); -#endif - -#if defined(NANA_MINGW) || defined(NANA_LINUX) - #if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ <= 1) - //Some functions which are specified in 21.5 Numeric conversions in Strings library have not yet implemented - #define STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED - #endif -#endif - -//Here defines some flags that tell Nana what features will be supported. -#define NANA_UNICODE - -#if defined(NANA_UNICODE) && defined(NANA_WINDOWS) - #ifndef _UNICODE - #define _UNICODE - #endif - - #ifndef UNICODE - #define UNICODE - #endif -#endif - -//Support for PNG -// Comment it to disable the feature of support for PNG. -//#define NANA_ENABLE_PNG -#if defined(NANA_ENABLE_PNG) - //Comment it to use libpng from operating system. - #define NANA_LIBPNG -#endif - +//All defines have been moved to the CMakelists.txt in the root directory. #endif //NANA_CONFIG_HPP diff --git a/include/nana/deploy.hpp b/include/nana/deploy.hpp index f51db6ef..b3ac88d0 100644 --- a/include/nana/deploy.hpp +++ b/include/nana/deploy.hpp @@ -19,7 +19,7 @@ #include #include -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) #undef NANA_WINDOWS #endif @@ -111,7 +111,7 @@ namespace nana #if defined(NANA_WINDOWS) #define NANA_SHARED_EXPORT extern "C" _declspec(dllexport) -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #define NANA_SHARED_EXPORT extern "C" #endif diff --git a/include/nana/detail/macos_X11/msg_packet.hpp b/include/nana/detail/macos_X11/msg_packet.hpp new file mode 100644 index 00000000..b1fc3dec --- /dev/null +++ b/include/nana/detail/macos_X11/msg_packet.hpp @@ -0,0 +1,32 @@ +#ifndef NANA_DETAIL_MSG_PACKET_HPP +#define NANA_DETAIL_MSG_PACKET_HPP +#include +#include +#include + +namespace nana +{ +namespace detail +{ + struct msg_packet_tag + { + enum kind_t{kind_xevent, kind_mouse_drop, kind_cleanup}; + kind_t kind; + union + { + XEvent xevent; + + Window packet_window; //Avaiable if the packet is not kind_xevent + struct mouse_drop_tag + { + Window window; + int x; + int y; + std::vector * files; + }mouse_drop; + }u; + }; +}//end namespace detail +}//end namespace nana +#endif + diff --git a/include/nana/detail/macos_X11/platform_spec.hpp b/include/nana/detail/macos_X11/platform_spec.hpp new file mode 100644 index 00000000..ae4a1830 --- /dev/null +++ b/include/nana/detail/macos_X11/platform_spec.hpp @@ -0,0 +1,325 @@ +/* + * Platform Specification Implementation + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/detail/platform_spec.hpp + * + * This file provides basis class and data structrue that required by nana + * This file should not be included by any header files. + */ + +#ifndef NANA_DETAIL_PLATFORM_SPEC_HPP +#define NANA_DETAIL_PLATFORM_SPEC_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "msg_packet.hpp" +#if defined(NANA_UNICODE) + #include + #include + #include +#endif + +namespace nana +{ +namespace detail +{ + class msg_dispatcher; +#if defined(NANA_UNICODE) + class conf + { + public: + conf(const char * file); + bool open(const char* file); + std::string value(const char* key); + private: + std::ifstream ifs_; + }; + + class charset_conv + { + public: + charset_conv(const char* tocode, const char* fromcode); + ~charset_conv(); + std::string charset(const std::string& str) const; + std::string charset(const char * buf, std::size_t len) const; + private: + iconv_t handle_; + }; +#endif + + struct font_tag + { + nana::string name; + unsigned height; + unsigned weight; + bool italic; + bool underline; + bool strikeout; +#if defined(NANA_UNICODE) + XftFont * handle; +#else + XFontSet handle; +#endif + }; + + struct drawable_impl_type + { + typedef std::shared_ptr font_ptr_t; + + Pixmap pixmap; + GC context; + font_ptr_t font; + + nana::point line_begin_pos; + + struct string_spec + { + unsigned tab_length; + unsigned tab_pixels; + unsigned whitespace_pixels; + }string; +#if defined(NANA_UNICODE) + XftDraw * xftdraw{nullptr}; + XftColor xft_fgcolor; + const std::string charset(const nana::string& str, const std::string& strcode); +#endif + drawable_impl_type(); + ~drawable_impl_type(); + + void fgcolor(const ::nana::color&); //deprecated + void set_color(const ::nana::color&); + void set_text_color(const ::nana::color&); + + void update_color(); + void update_text_color(); + private: + unsigned current_color_{ 0xFFFFFF }; + unsigned color_{ 0xFFFFFFFF }; + unsigned text_color_{ 0xFFFFFFFF }; + +#if defined(NANA_UNICODE) + struct conv_tag + { + iconv_t handle; + std::string code; + }conv_; +#endif + }; + + struct atombase_tag + { + Atom wm_protocols; + //window manager support + Atom wm_change_state; + Atom wm_delete_window; + //ext + Atom net_wm_state; + Atom net_wm_state_skip_taskbar; + Atom net_wm_state_fullscreen; + Atom net_wm_state_maximized_horz; + Atom net_wm_state_maximized_vert; + Atom net_wm_state_modal; + Atom net_wm_name; + Atom net_wm_window_type; + Atom net_wm_window_type_normal; + Atom net_wm_window_type_utility; + Atom net_wm_window_type_dialog; + Atom motif_wm_hints; + + Atom clipboard; + Atom text; + Atom text_uri_list; + Atom utf8_string; + Atom targets; + + Atom xdnd_aware; + Atom xdnd_enter; + Atom xdnd_position; + Atom xdnd_status; + Atom xdnd_action_copy; + Atom xdnd_drop; + Atom xdnd_selection; + Atom xdnd_typelist; + Atom xdnd_finished; + }; + + struct caret_tag; + + class timer_runner; + + class platform_scope_guard + { + public: + platform_scope_guard(); + ~platform_scope_guard(); + }; + + class platform_spec + { + typedef platform_spec self_type; + + struct window_context_t + { + native_window_type owner; + std::vector * owned; + }; + public: + int error_code; + public: + typedef drawable_impl_type::font_ptr_t font_ptr_t; + typedef void (*timer_proc_type)(unsigned tid); + typedef void (*event_proc_type)(Display*, msg_packet_tag&); + typedef ::nana::event_code event_code; + typedef ::nana::native_window_type native_window_type; + + + platform_spec(); + ~platform_spec(); + + const font_ptr_t& default_native_font() const; + void default_native_font(const font_ptr_t&); + unsigned font_size_to_height(unsigned) const; + unsigned font_height_to_size(unsigned) const; + font_ptr_t make_native_font(const nana::char_t* name, unsigned height, unsigned weight, bool italic, bool underline, bool strick_out); + + Display* open_display(); + void close_display(); + + void lock_xlib(); + void unlock_xlib(); + + Window root_window(); + int screen_depth(); + Visual* screen_visual(); + + Colormap& colormap(); + + static self_type& instance(); + const atombase_tag & atombase() const; + + void make_owner(native_window_type owner, native_window_type wd); + native_window_type get_owner(native_window_type) const; + void remove(native_window_type); + + void write_keystate(const XKeyEvent&); + void read_keystate(XKeyEvent&); + + XIC caret_input_context(native_window_type) const; + void caret_open(native_window_type, const ::nana::size&); + void caret_close(native_window_type); + void caret_pos(native_window_type, const ::nana::point&); + void caret_visible(native_window_type, bool); + void caret_flash(caret_tag&); + bool caret_update(native_window_type, nana::paint::graphics& root_graph, bool is_erase_caret_from_root_graph); + static bool caret_reinstate(caret_tag&); + void set_error_handler(); + int rev_error_handler(); + + //grab + //register a grab window while capturing it if it is unviewable. + //when native_interface::show a window that is registered as a grab + //window, the native_interface grabs the window. + Window grab(Window); + void set_timer(std::size_t id, std::size_t interval, void (*timer_proc)(std::size_t id)); + void kill_timer(std::size_t id); + void timer_proc(unsigned tid); + + //Message dispatcher + void msg_insert(native_window_type); + void msg_set(timer_proc_type, event_proc_type); + void msg_dispatch(native_window_type modal); + + //X Selections + void* request_selection(native_window_type requester, Atom type, size_t & bufsize); + void write_selection(native_window_type owner, Atom type, const void* buf, size_t bufsize); + + //Icon storage + //@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&); + private: + static int _m_msg_filter(XEvent&, msg_packet_tag&); + void _m_caret_routine(); + private: + Display* display_; + Colormap colormap_; + atombase_tag atombase_; + font_ptr_t def_font_ptr_; + XKeyEvent key_state_; + int (*def_X11_error_handler_)(Display*, XErrorEvent*); + Window grab_; + std::recursive_mutex xlib_locker_; + struct caret_holder_tag + { + volatile bool exit_thread; + std::unique_ptr thr; + std::map carets; + }caret_holder_; + + std::map wincontext_; + std::map iconbase_; + + struct timer_runner_tag + { + timer_runner * runner; + std::recursive_mutex mutex; + bool delete_declared; + timer_runner_tag(); + }timer_; + + struct selection_tag + { + struct item_t + { + Atom type; + Window requestor; + void* buffer; + size_t bufsize; + std::mutex cond_mutex; + std::condition_variable cond; + }; + + std::vector items; + + struct content_tag + { + std::string * utf8_string; + }content; + }selection_; + + struct xdnd_tag + { + Atom good_type; + int timestamp; + Window wd_src; + nana::point pos; + }xdnd_; + + msg_dispatcher * msg_dispatcher_; + };//end class platform_X11 + +}//end namespace detail + +}//end namespace nana + +#endif + diff --git a/include/nana/filesystem/file_iterator.hpp b/include/nana/filesystem/file_iterator.hpp index a4801571..b884638f 100644 --- a/include/nana/filesystem/file_iterator.hpp +++ b/include/nana/filesystem/file_iterator.hpp @@ -22,7 +22,7 @@ #ifdef NANA_WINDOWS #include typedef HANDLE find_handle_t; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #include #include @@ -38,7 +38,7 @@ namespace filesystem fileinfo(); #ifdef NANA_WINDOWS fileinfo(const WIN32_FIND_DATA& wfd); -#elif NANA_LINUX +#elif NANA_LINUX or NANA_MACOS fileinfo(const nana::string& filename, const struct stat &); #endif nana::string name; @@ -119,7 +119,7 @@ namespace filesystem } } value_ = value_type(wfd_); - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) path_ = nana::charset(file_path); if(path_.size() && (path_[path_.size() - 1] != '/')) path_ += '/'; @@ -181,7 +181,7 @@ namespace filesystem } else end_ = true; - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) struct dirent * dnt = readdir(handle_); if(dnt) { @@ -214,7 +214,7 @@ namespace filesystem { #if defined(NANA_WINDOWS) ::FindClose(*handle); - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) ::closedir(*handle); #endif } @@ -227,7 +227,7 @@ namespace filesystem #if defined(NANA_WINDOWS) WIN32_FIND_DATA wfd_; nana::string path_; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) std::string path_; #endif std::shared_ptr find_ptr_; diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 6647747d..dda69c98 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -42,7 +42,7 @@ #ifdef NANA_WINDOWS #include typedef HANDLE find_handle_t; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #include #include @@ -264,7 +264,7 @@ namespace filesystem (FILE_ATTRIBUTE_DIRECTORY & wfd_.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY, wfd_.nFileSizeLow); - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) path_ = nana::charset(file_path); if(path_.size() && (path_[path_.size() - 1] != '/')) path_ += '/'; @@ -326,7 +326,7 @@ namespace filesystem } else end_ = true; - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) struct dirent * dnt = readdir(handle_); if(dnt) { @@ -361,7 +361,7 @@ namespace filesystem { #if defined(NANA_WINDOWS) ::FindClose(*handle); - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) ::closedir(*handle); #endif } @@ -374,7 +374,7 @@ namespace filesystem #if defined(NANA_WINDOWS) WIN32_FIND_DATA wfd_; nana::string path_; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) std::string path_; #endif std::shared_ptr find_ptr_; diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 1ce944a9..ac5db7c1 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -128,7 +128,7 @@ namespace detail void _m_init_pos_and_size(basic_window* parent, const rectangle&); void _m_initialize(basic_window* parent); public: -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) point pos_native; #endif point pos_root; //coordinate for root window diff --git a/source/charset.cpp b/source/charset.cpp index 7e952503..1b57ad29 100644 --- a/source/charset.cpp +++ b/source/charset.cpp @@ -284,7 +284,7 @@ namespace nana std::u32string utf32str = std::wstring_convert, char32_t>().from_bytes(bytes, bytes + sizeof(wchar_t) * wcstr.size()); return std::string(reinterpret_cast(utf32str.c_str()), sizeof(char32_t) * utf32str.size()); } - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) return std::string(reinterpret_cast(wcstr.c_str()), sizeof(wchar_t) * wcstr.size()); #else throw std::runtime_error("Bad charset"); @@ -376,7 +376,7 @@ namespace nana std::u32string utf32str = std::wstring_convert, char32_t>().from_bytes(bytes, bytes + sizeof(wchar_t) * data_.size()); return std::string(reinterpret_cast(utf32str.c_str()), sizeof(char32_t) * utf32str.size()); } - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) return std::string(reinterpret_cast(data_.c_str()), data_.size() * sizeof(wchar_t)); #else throw std::runtime_error("Bad charset"); diff --git a/source/deploy.cpp b/source/deploy.cpp index c4d4d3f8..0f5c8498 100644 --- a/source/deploy.cpp +++ b/source/deploy.cpp @@ -18,7 +18,7 @@ #if defined(NANA_WINDOWS) #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #include PLATFORM_SPEC_HPP #endif diff --git a/source/detail/macos_X11/msg_dispatcher.hpp b/source/detail/macos_X11/msg_dispatcher.hpp new file mode 100644 index 00000000..7ba64b4f --- /dev/null +++ b/source/detail/macos_X11/msg_dispatcher.hpp @@ -0,0 +1,341 @@ +/* + * Message Dispatcher Implementation + * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/detail/msg_dispatcher.hpp + * + * @DO NOT INCLUDE THIS HEADER FILE IN YOUR SOURCE FILE!! + * + * This class msg_dispatcher provides a simulation of Windows-like message + * dispatcher. Every event is dispatched into its own message queue for + * corresponding thread. + */ + +#ifndef NANA_DETAIL_MSG_DISPATCHER_HPP +#define NANA_DETAIL_MSG_DISPATCHER_HPP +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nana +{ +namespace detail +{ + class msg_dispatcher + { + struct thread_binder + { + unsigned tid; + std::mutex mutex; + std::condition_variable cond; + std::list msg_queue; + std::set window; + }; + + public: + typedef msg_packet_tag msg_packet; + typedef void (*timer_proc_type)(unsigned tid); + typedef void (*event_proc_type)(Display*, msg_packet_tag&); + typedef int (*event_filter_type)(XEvent&, msg_packet_tag&); + + typedef std::list msg_queue_type; + + msg_dispatcher(Display* disp) + : display_(disp), is_work_(false) + { + proc_.event_proc = 0; + proc_.timer_proc = 0; + proc_.filter_proc = 0; + } + + void set(timer_proc_type timer_proc, event_proc_type event_proc, event_filter_type filter) + { + proc_.timer_proc = timer_proc; + proc_.event_proc = event_proc; + proc_.filter_proc = filter; + } + + void insert(Window wd) + { + unsigned tid = nana::system::this_thread_id(); + + bool start_driver; + + { + std::lock_guard lock(table_.mutex); + + //No thread is running, so msg dispatcher should start the msg driver. + start_driver = (0 == table_.thr_table.size()); + thread_binder * thr; + + std::map::iterator i = table_.thr_table.find(tid); + if(i == table_.thr_table.end()) + { + thr = new thread_binder; + thr->tid = tid; + table_.thr_table.insert(std::make_pair(tid, thr)); + } + else + thr = i->second; + + thr->mutex.lock(); + thr->window.insert(wd); + thr->mutex.unlock(); + + table_.wnd_table[wd] = thr; + } + + if(start_driver && proc_.event_proc && proc_.timer_proc) + { + //It should start the msg driver, before starting it, the msg driver must be inactive. + if(thrd_) + { + is_work_ = false; + thrd_->join(); + } + is_work_ = true; + thrd_ = std::unique_ptr(new std::thread([this](){ this->_m_msg_driver(); })); + } + } + + void erase(Window wd) + { + std::lock_guard lock(table_.mutex); + + auto i = table_.wnd_table.find(wd); + if(i != table_.wnd_table.end()) + { + thread_binder * const thr = i->second; + std::lock_guardmutex)> lock(thr->mutex); + for(auto li = thr->msg_queue.begin(); li != thr->msg_queue.end();) + { + if(wd == _m_window(*li)) + li = thr->msg_queue.erase(li); + else + ++li; + } + + table_.wnd_table.erase(i); + thr->window.erase(wd); + + //There still is at least one window alive. + if(thr->window.size()) + { + //Make a cleanup msg packet to infor the dispatcher the window is closed. + msg_packet_tag msg; + msg.kind = msg.kind_cleanup; + msg.u.packet_window = wd; + thr->msg_queue.push_back(msg); + } + } + } + + void dispatch(Window modal) + { + unsigned 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, modal))) + { + //the queue is empty + if(-1 == qstate) + { + if(false == _m_wait_for_queue(tid)) + proc_.timer_proc(tid); + } + else + { + proc_.event_proc(display_, msg); + } + } + } + private: + void _m_msg_driver() + { + int fd_X11 = ConnectionNumber(display_); + + msg_packet_tag msg_pack; + XEvent event; + while(is_work_) + { + int pending; + { + nana::detail::platform_scope_guard lock; + pending = ::XPending(display_); + if(pending) + { + ::XNextEvent(display_, &event); + if(::XFilterEvent(&event, None)) + continue; + } + } + + if(0 == pending) + { + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(fd_X11, &fdset); + + struct timeval tv; + tv.tv_usec = 10000; + tv.tv_sec = 0; + ::select(fd_X11 + 1, &fdset, 0, 0, &tv); + } + else + { + switch(proc_.filter_proc(event, msg_pack)) + { + case 0: + msg_pack.kind = msg_pack.kind_xevent; + msg_pack.u.xevent = event; + case 1: + _m_msg_dispatch(msg_pack); + } + } + } + } + private: + static Window _m_event_window(const XEvent& event) + { + switch(event.type) + { + case MapNotify: + case UnmapNotify: + return event.xmap.window; + } + return event.xkey.window; + } + + static Window _m_window(const msg_packet_tag& pack) + { + switch(pack.kind) + { + case msg_packet_tag::kind_xevent: + return _m_event_window(pack.u.xevent); + case msg_packet_tag::kind_mouse_drop: + return pack.u.mouse_drop.window; + default: + break; + } + return 0; + } + + void _m_msg_dispatch(const msg_packet_tag &msg) + { + std::lock_guard lock(table_.mutex); + auto i = table_.wnd_table.find(_m_window(msg)); + if(i != table_.wnd_table.end()) + { + thread_binder * const thr = i->second; + + std::lock_guardmutex)> lock(thr->mutex); + thr->msg_queue.push_back(msg); + thr->cond.notify_one(); + } + } + + //_m_read_queue + //@brief:Read the event from a specified thread queue. + //@return: 0 = exit the queue, 1 = fetch the msg, -1 = no msg + int _m_read_queue(unsigned tid, msg_packet_tag& msg, Window modal) + { + bool stop_driver = false; + + { + std::lock_guard lock(table_.mutex); + //Find the thread whether it is registered for the window. + auto i = table_.thr_table.find(tid); + if(i != table_.thr_table.end()) + { + if(i->second->window.size()) + { + msg_queue_type & queue = i->second->msg_queue; + if(queue.size()) + { + msg = queue.front(); + queue.pop_front(); + + //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)) + return 0; + + return 1; + } + else + return -1; + } + + delete i->second; + table_.thr_table.erase(i); + stop_driver = (table_.thr_table.size() == 0); + } + } + if(stop_driver) + { + is_work_ = false; + thrd_->join(); + thrd_.reset(); + } + return 0; + } + + //_m_wait_for_queue + // wait for the insertion of queue. + //return@ it returns true if the queue is not empty, otherwise the wait is timeout. + bool _m_wait_for_queue(unsigned tid) + { + thread_binder * thr = nullptr; + { + std::lock_guard lock(table_.mutex); + auto i = table_.thr_table.find(tid); + if(i != table_.thr_table.end()) + { + if(i->second->msg_queue.size()) + return true; + thr = i->second; + } + } + + //Waits for notifying the condition variable, it indicates a new msg is pushing into the queue. + std::unique_lockmutex)> lock(thr->mutex); + return (thr->cond.wait_for(lock, std::chrono::milliseconds(10)) != std::cv_status::timeout); + } + + private: + Display * display_; + volatile bool is_work_; + std::unique_ptr thrd_; + + struct table_tag + { + std::recursive_mutex mutex; + std::map thr_table; + std::map wnd_table; + }table_; + + struct proc_tag + { + timer_proc_type timer_proc; + event_proc_type event_proc; + event_filter_type filter_proc; + }proc_; + }; +}//end namespace detail +}//end namespace nana + +#endif + diff --git a/source/detail/macos_X11/platform_spec.cpp b/source/detail/macos_X11/platform_spec.cpp new file mode 100644 index 00000000..5e4d8683 --- /dev/null +++ b/source/detail/macos_X11/platform_spec.cpp @@ -0,0 +1,1396 @@ +/* + * Platform Specification Implementation + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 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 + +#include PLATFORM_SPEC_HPP +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msg_dispatcher.hpp" + +namespace nana +{ +namespace detail +{ + typedef native_window_type native_window_type; +#if defined(NANA_UNICODE) + //class conf + conf::conf(const char * file) + { + ifs_.open(file); + } + + bool conf::open(const char* file) + { + ifs_.open(file); + return static_cast(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(-1) == handle_) + return std::string(); + + char * inbuf = const_cast(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(-1) == handle_) + return std::string(); + + char * inbuf = const_cast(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 + + struct caret_tag + { + native_window_type window; + bool has_input_method_focus; + bool visible; + nana::point pos; + nana::size size; + nana::rectangle rev; + nana::paint::graphics graph; + nana::paint::graphics rev_graph; + XIM input_method; + XIC input_context; + XFontSet input_font; + XRectangle input_spot; + XRectangle input_status_area; + long input_context_event_mask; + + caret_tag(native_window_type wd) + : window(wd), has_input_method_focus(false), visible(false), input_method(0), input_context(0), input_font(0), input_context_event_mask(0) + {} + }; + + class timer_runner + { + typedef void (*timer_proc_t)(std::size_t id); + + struct timer_tag + { + std::size_t id; + unsigned 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 timers; + std::vector 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; + } + unsigned 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(unsigned 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 threadmap_; + std::map holder_; + }; + + drawable_impl_type::drawable_impl_type() + { + string.tab_length = 4; + string.tab_pixels = 0; + string.whitespace_pixels = 0; +#if defined(NANA_UNICODE) + conv_.handle = ::iconv_open("UTF-8", "UTF-32"); + conv_.code = "UTF-32"; +#endif + } + + drawable_impl_type::~drawable_impl_type() + { +#if defined(NANA_UNICODE) + ::iconv_close(conv_.handle); +#endif + } + + void drawable_impl_type::set_color(const ::nana::color& clr) + { + color_ = clr.px_color().value; + } + + void drawable_impl_type::set_text_color(const ::nana::color& clr) + { + text_color_ = clr.px_color().value; + 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_UNICODE) + 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 + } + } + + void drawable_impl_type::fgcolor(const ::nana::color& clr) + { + auto rgb = clr.px_color().value; + + if (rgb != current_color_) + { + auto & spec = nana::detail::platform_spec::instance(); + platform_scope_guard psg; + + current_color_ = rgb; + switch(spec.screen_depth()) + { + case 16: + rgb = ((((rgb >> 16) & 0xFF) * 31 / 255) << 11) | + ((((rgb >> 8) & 0xFF) * 63 / 255) << 5) | + (rgb & 0xFF) * 31 / 255; + break; + } + ::XSetForeground(spec.open_display(), context, rgb); + ::XSetBackground(spec.open_display(), context, rgb); +#if defined(NANA_UNICODE) + xft_fgcolor.color.red = ((0xFF0000 & rgb) >> 16) * 0x101; + xft_fgcolor.color.green = ((0xFF00 & rgb) >> 8) * 0x101; + xft_fgcolor.color.blue = (0xFF & rgb) * 0x101; + xft_fgcolor.color.alpha = 0xFFFF; +#endif + } + } + + class font_deleter + { + public: + void operator()(const font_tag* fp) const + { + if(fp && fp->handle) + { + platform_scope_guard psg; +#if defined(NANA_UNICODE) + ::XftFontClose(nana::detail::platform_spec::instance().open_display(), fp->handle); +#else + ::XFreeFontSet(nana::detail::platform_spec::instance().open_display(), fp->handle); +#endif + } + delete fp; + } + };//end class font_deleter + + 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* disp, XErrorEvent* err) + { + platform_spec::instance().error_code = err->error_code; + return 0; + } + + int X11_fatal_handler(Display* disp) + { + 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"; + 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_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); + + //Create default font object. + def_font_ptr_ = make_native_font(0, font_size_to_height(10), 400, false, false, false); + msg_dispatcher_ = new msg_dispatcher(display_); + } + + platform_spec::~platform_spec() + { + delete msg_dispatcher_; + + //The font should be destroyed before closing display, + //otherwise it crashs + def_font_ptr_.reset(); + + close_display(); + } + + const platform_spec::font_ptr_t& platform_spec::default_native_font() const + { + return def_font_ptr_; + } + + void platform_spec::default_native_font(const font_ptr_t& fp) + { + def_font_ptr_ = fp; + } + + unsigned platform_spec::font_size_to_height(unsigned size) const + { + return size; + } + + unsigned platform_spec::font_height_to_size(unsigned height) const + { + return height; + } + + platform_spec::font_ptr_t platform_spec::make_native_font(const nana::char_t* name, unsigned height, unsigned weight, bool italic, bool underline, bool strike_out) + { + font_ptr_t ref; +#if defined(NANA_UNICODE) + if(0 == name || *name == 0) + name = STR("*"); + + std::string nmstr = nana::charset(name); + XftFont* handle = 0; + std::stringstream ss; + ss<(basestr.c_str()), &missing_list, &missing_count, &defstr); +#endif + if(handle) + { + font_tag * impl = new font_tag; + impl->name = name; + impl->height = height; + impl->weight = weight; + impl->italic = italic; + impl->underline = underline; + impl->strikeout = strike_out; + impl->handle = handle; + return font_ptr_t(impl, font_deleter()); + } + return font_ptr_t(); + } + + Display* platform_spec::open_display() + { + return display_; + } + + void platform_spec::close_display() + { + if(display_) + { + ::XSync(reinterpret_cast(display_), true); + ::XCloseDisplay(reinterpret_cast(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_)); + } + + 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 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 psg; + wincontext_[wd].owner = owner; + window_context_t & context = wincontext_[owner]; + if(context.owned == 0) + context.owned = new std::vector; + context.owned->push_back(wd); + } + + 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(wd)); + platform_scope_guard psg; + auto i = wincontext_.find(wd); + if(i == wincontext_.end()) return; + + if(i->second.owner) + { + auto u = wincontext_.find(i->second.owner); + if(u != wincontext_.end()) + { + auto * vec = u->second.owned; + if(vec) + { + auto j = std::find(vec->begin(), vec->end(), i->first); + if(j != vec->end()) + vec->erase(j); + } + } + } + + auto * vec = i->second.owned; + if(vec) + { + set_error_handler(); + auto & wd_manager = detail::bedrock::instance().wd_manager; + for(auto u = vec->rbegin(); u != vec->rend(); ++u) + wd_manager.close(wd_manager.root(*u)); + + rev_error_handler(); + } + delete vec; + 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; + caret_tag * & addr = caret_holder_.carets[wd]; + if(0 == addr) + { + addr = new caret_tag(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(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(wd), nullptr); + } + + if(addr->input_context) + { + XVaNestedList attr = ::XVaCreateNestedList(0, XNAreaNeeded, &(addr->input_status_area), XNClientWindow, reinterpret_cast(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(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(wd), &attr); + XSetWindowAttributes new_attr; + new_attr.event_mask = (attr.your_event_mask | addr->input_context_event_mask); + ::XChangeWindowAttributes(display_, reinterpret_cast(wd), CWEventMask, &new_attr); + } + ::XFree(preedit_attr); + ::XFree(status_attr); + } + ::XFree(imstyle); + } + } + } + + addr->visible = false; + addr->graph.make(caret_sz); + addr->graph.rectangle(true, colors::black); + 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()) + { + caret_tag * 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(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(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()) + { + caret_tag & crt = *i->second; + caret_reinstate(crt); + crt.pos = pos; + } + } + + 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()) + { + caret_tag& crt = *i->second; + if(crt.visible != vis) + { + if(vis == false) + { + caret_reinstate(crt); + 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; + } + } + } + + void platform_spec::caret_flash(caret_tag & crt) + { + if(crt.visible && (false == caret_reinstate(crt))) + { + crt.rev_graph.bitblt(rectangle{crt.size}, crt.window, crt.pos); + crt.rev.width = crt.size.width; + crt.rev.height = crt.size.height; + crt.rev.x = crt.pos.x; + crt.rev.y = crt.pos.y; + crt.graph.paste(crt.window, crt.rev, 0, 0); + } + } + + bool platform_spec::caret_update(native_window_type wd, nana::paint::graphics& root_graph, bool is_erase_caret_from_root_graph) + { + platform_scope_guard psg; + auto i = caret_holder_.carets.find(wd); + if(i != caret_holder_.carets.end()) + { + caret_tag & crt = *i->second; + if(is_erase_caret_from_root_graph) + { + root_graph.bitblt(crt.rev, crt.rev_graph); + } + else + { + bool owns_caret = false; + nana::paint::graphics * crt_graph; + if(crt.rev.width && crt.rev.height) + { + crt.rev_graph.bitblt(rectangle{crt.size}, root_graph, crt.pos); + crt_graph = &crt.graph; + owns_caret = true; + } + else + crt_graph = &crt.rev_graph; + + root_graph.bitblt(crt.rev, *crt_graph); + return owns_caret; + } + } + return false; + } + + //Copy the reversed graphics to the window + bool platform_spec::caret_reinstate(caret_tag& crt) + { + if(crt.rev.width && crt.rev.height) + { + crt.rev_graph.paste(crt.window, crt.rev, 0, 0); + //Drop the reversed graphics in order to draw the + //caret in the next flash. + crt.rev.width = crt.rev.height = 0; + return true; + } + 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) + caret_flash(*(i.second)); + + 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 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 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(unsigned tid) + { + std::lock_guard 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(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(modal)); + } + + 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(requestor); + selim->buffer = nullptr; + selim->bufsize = 0; + + this->selection_.items.push_back(selim); + ::XConvertSelection(display_, clipboard, type, clipboard, + reinterpret_cast(requestor), CurrentTime); + ::XFlush(display_); + xlib_locker_.unlock(); + + std::unique_lockcond_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_, atombase_.clipboard, reinterpret_cast(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(buf), reinterpret_cast(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; + } + + //_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) + { + platform_spec & self = instance(); + 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_guardcond_mutex)> lock(im->cond_mutex); + im->cond.notify_one(); + + } + } + else if(evt.xselection.property == self.atombase_.xdnd_selection) + { + bool accepted = false; + msg.kind = msg.kind_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)) + { + std::vector * files = new std::vector; + std::stringstream ss(reinterpret_cast(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(nana::charset(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; + } + + 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); + + if(msg.u.mouse_drop.window) + return 1; //Use the packet directly. + } + } + 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 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, sizeof(Atom) * 8, 0, + reinterpret_cast(atoms.size() ? &atoms[0] : 0), static_cast(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(str.size() ? const_cast(str.c_str()) : 0), static_cast(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(&(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(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); + typedef detail::bedrock bedrock; + + auto wd = bedrock::instance().wd_manager.find_window(reinterpret_cast(evt.xclient.window), self.xdnd_.pos.x, self.xdnd_.pos.y); + 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 diff --git a/source/detail/platform_spec_selector.cpp b/source/detail/platform_spec_selector.cpp index 585646ab..895ded7c 100644 --- a/source/detail/platform_spec_selector.cpp +++ b/source/detail/platform_spec_selector.cpp @@ -19,4 +19,6 @@ #include "win32/platform_spec.cpp" #elif defined(NANA_LINUX) #include "linux_X11/platform_spec.cpp" +#elif defined(NANA_MACOS) + #include "macos_X11/platform_spec.cpp" #endif \ No newline at end of file diff --git a/source/filesystem/file_iterator.cpp b/source/filesystem/file_iterator.cpp index d77900e1..8880f77d 100644 --- a/source/filesystem/file_iterator.cpp +++ b/source/filesystem/file_iterator.cpp @@ -28,7 +28,7 @@ namespace filesystem directory((FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY) { } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) fileinfo::fileinfo(const nana::string& name, const struct stat& fst) :name(name), size(fst.st_size), directory(0 != S_ISDIR(fst.st_mode)) { diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index 51d70ff4..ae00b7cd 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -24,7 +24,7 @@ #include #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #include #include @@ -42,7 +42,7 @@ namespace nana { namespace filesystem { //Because of No wide character version of POSIX -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) typedef std::string string_t; const char* splstr = "/\\"; #else @@ -69,7 +69,7 @@ namespace nana { { #if defined(NANA_WINDOWS) return (::GetFileAttributes(text_.c_str()) == INVALID_FILE_ATTRIBUTES); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat sta; return (::stat(text_.c_str(), &sta) == -1); #endif @@ -79,7 +79,7 @@ namespace nana { { #if defined(NANA_WINDOWS) return path(filesystem::root(text_)); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) return path(filesystem::root(nana::charset(text_))); #endif } @@ -95,7 +95,7 @@ namespace nana { return file_type::directory; return file_type::regular; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat sta; if (-1 == ::stat(text_.c_str(), &sta)) return file_type::not_found; //?? @@ -154,7 +154,7 @@ namespace nana { } if_exist = (::GetLastError() == ERROR_ALREADY_EXISTS); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) if (0 == ::mkdir(static_cast(nana::charset(dir)).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { if_exist = false; @@ -202,7 +202,7 @@ namespace nana { detail::filetime_to_c_tm(fad.ftLastWriteTime, attr.modified); return true; } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat fst; if (0 == ::stat(static_cast(nana::charset(file)).c_str(), &fst)) { @@ -245,6 +245,16 @@ namespace nana { fclose(stream); } return size; +#elif defined(NANA_MACOS) + FILE * stream = ::fopen(static_cast(nana::charset(file)).c_str(), "rb"); + long long size = 0; + if (stream) + { + fseeko(stream, 0, SEEK_END); + size = ftello(stream); + fclose(stream); + } + return size; #endif } @@ -271,7 +281,7 @@ namespace nana { return true; } } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat attr; if (0 == ::stat(static_cast(nana::charset(file)).c_str(), &attr)) { @@ -291,7 +301,7 @@ namespace nana { #if defined(NANA_WINDOWS) if (path.size() > 3 && path[1] == STR(':')) root = path.substr(0, 3); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) if (path[0] == STR('/')) root = '/'; #endif @@ -315,7 +325,7 @@ namespace nana { #if defined(NANA_WINDOWS) root += STR('\\'); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) root += STR('/'); #endif } @@ -345,7 +355,7 @@ namespace nana { } return ret; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) if (std::remove(static_cast(nana::charset(file)).c_str())) return (errno == ENOENT); return true; @@ -361,7 +371,7 @@ namespace nana { ret = (::RemoveDirectory(dir) == TRUE); if (!fails_if_not_empty && (::GetLastError() == ERROR_DIR_NOT_EMPTY)) ret = detail::rm_dir_recursive(dir); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) std::string mbstr = nana::charset(dir); if (::rmdir(mbstr.c_str())) { @@ -407,7 +417,7 @@ namespace nana { nana::char_t path[MAX_PATH]; if (SUCCEEDED(SHGetFolderPath(0, CSIDL_PROFILE, 0, SHGFP_TYPE_CURRENT, path))) return path; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) const char * s = ::getenv("HOME"); if (s) return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8); @@ -432,7 +442,7 @@ namespace nana { } return buf; } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) const char * s = ::getenv("PWD"); if (s) return static_cast(nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8)); diff --git a/source/filesystem/fs_utility.cpp b/source/filesystem/fs_utility.cpp index d1f95e69..c9cfd677 100644 --- a/source/filesystem/fs_utility.cpp +++ b/source/filesystem/fs_utility.cpp @@ -25,7 +25,7 @@ #include #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #include #include @@ -42,7 +42,7 @@ namespace nana namespace filesystem { //Because of No wide character version of POSIX -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) typedef std::string string_t; const char* splstr = "/\\"; #else @@ -69,7 +69,7 @@ namespace filesystem { #if defined(NANA_WINDOWS) return (::GetFileAttributes(text_.c_str()) == INVALID_FILE_ATTRIBUTES); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat sta; return (::stat(text_.c_str(), &sta) == -1); #endif @@ -79,7 +79,7 @@ namespace filesystem { #if defined(NANA_WINDOWS) return path(filesystem::root(text_)); - #elif defined(NANA_LINUX) + #elif defined(NANA_LINUX) || defined(NANA_MACOS) return path(filesystem::root(nana::charset(text_))); #endif } @@ -95,7 +95,7 @@ namespace filesystem return type::directory; return type::file; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat sta; if(-1 == ::stat(text_.c_str(), &sta)) return type::not_exist; @@ -154,7 +154,7 @@ namespace filesystem } if_exist = (::GetLastError() == ERROR_ALREADY_EXISTS); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) if(0 == ::mkdir(static_cast(nana::charset(dir)).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { if_exist = false; @@ -202,7 +202,7 @@ namespace filesystem detail::filetime_to_c_tm(fad.ftLastWriteTime, attr.modified); return true; } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat fst; if(0 == ::stat(static_cast(nana::charset(file)).c_str(), &fst)) { @@ -245,6 +245,16 @@ namespace filesystem fclose(stream); } return size; +#elif defined(NANA_MACOS) + FILE * stream = ::fopen(static_cast(nana::charset(file)).c_str(), "rb"); + long long size = 0; + if (stream) + { + fseeko(stream, 0, SEEK_END); + size = ftello(stream); + fclose(stream); + } + return size; #endif } @@ -271,7 +281,7 @@ namespace filesystem return true; } } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct stat attr; if(0 == ::stat(static_cast(nana::charset(file)).c_str(), &attr)) { @@ -291,7 +301,7 @@ namespace filesystem #if defined(NANA_WINDOWS) if(path.size() > 3 && path[1] == STR(':')) root = path.substr(0, 3); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) if(path[0] == STR('/')) root = '/'; #endif @@ -315,7 +325,7 @@ namespace filesystem #if defined(NANA_WINDOWS) root += STR('\\'); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) root += STR('/'); #endif } @@ -345,7 +355,7 @@ namespace filesystem } return ret; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) if(std::remove(static_cast(nana::charset(file)).c_str())) return (errno == ENOENT); return true; @@ -361,7 +371,7 @@ namespace filesystem ret = (::RemoveDirectory(dir) == TRUE); if(!fails_if_not_empty && (::GetLastError() == ERROR_DIR_NOT_EMPTY)) ret = detail::rm_dir_recursive(dir); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) std::string mbstr = nana::charset(dir); if(::rmdir(mbstr.c_str())) { @@ -407,7 +417,7 @@ namespace filesystem nana::char_t path[MAX_PATH]; if(SUCCEEDED(SHGetFolderPath(0, CSIDL_PROFILE, 0, SHGFP_TYPE_CURRENT, path))) return path; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) const char * s = ::getenv("HOME"); if(s) return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8); @@ -432,7 +442,7 @@ namespace filesystem } return buf; } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) const char * s = ::getenv("PWD"); if(s) return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8); diff --git a/source/gui/detail/bedrock_selector.cpp b/source/gui/detail/bedrock_selector.cpp index 3d10fd7b..986d3538 100644 --- a/source/gui/detail/bedrock_selector.cpp +++ b/source/gui/detail/bedrock_selector.cpp @@ -17,7 +17,7 @@ #if defined(NANA_WINDOWS) #include "win32/bedrock.cpp" -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include "linux_X11/bedrock.cpp" #endif diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index e9581fd1..2c5f3184 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -675,7 +675,7 @@ namespace detail if (impl_->wd_register.available(wd) && !wd->is_draw_through()) { //Copy the root buffer that wd specified into DeviceContext -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) wd->drawer.map(reinterpret_cast(wd), forced, update_area); #elif defined(NANA_WINDOWS) if(nana::system::this_thread_id() == wd->thread_id) diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 441d5682..ef5a4d52 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -16,7 +16,7 @@ #if defined(NANA_WINDOWS) #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #include #include @@ -32,7 +32,7 @@ namespace nana { -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) class filebox_implement : public form { @@ -1027,7 +1027,7 @@ namespace nana return false; impl_->file.resize(nana::strlen(impl_->file.data())); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) filebox_implement fb(impl_->owner, impl_->open_or_save, impl_->title); if(impl_->filters.size()) diff --git a/source/gui/notifier.cpp b/source/gui/notifier.cpp index aec5686f..1146cdc2 100644 --- a/source/gui/notifier.cpp +++ b/source/gui/notifier.cpp @@ -26,7 +26,7 @@ #if defined(NANA_WINDOWS) #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include PLATFORM_SPEC_HPP #include #include diff --git a/source/gui/timer.cpp b/source/gui/timer.cpp index 75181a7f..653d4714 100644 --- a/source/gui/timer.cpp +++ b/source/gui/timer.cpp @@ -26,7 +26,7 @@ #if defined(NANA_WINDOWS) #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include PLATFORM_SPEC_HPP #include #endif diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index c9402b8c..5db0dac8 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -901,7 +901,7 @@ namespace nana /// add a new cat created at "pos" and return a ref to it category_t* create_cat(std::size_t pos, nana::string&& text) { -#if defined(NANA_LINUX) || defined(NANA_MINGW) +#if defined(NANA_LINUX) || defined(NANA_MACOS) || defined(NANA_MINGW) //Call begin instead of cbegin, because the first parameter //of emplace is not const_iterator in GCC's C++ standard //library implementation. diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index cba4b272..48a1e7a1 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -933,7 +933,7 @@ namespace paint { const nana::char_t * end = str + len; const nana::char_t * i = std::find(str, end, '\t'); -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) handle_->update_text_color(); #endif if (i != end) diff --git a/source/system/platform.cpp b/source/system/platform.cpp index 2985e831..6a9ac116 100644 --- a/source/system/platform.cpp +++ b/source/system/platform.cpp @@ -15,7 +15,7 @@ #if defined(NANA_WINDOWS) #include #include PLATFORM_SPEC_HPP -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #include #include @@ -34,7 +34,7 @@ namespace system { #if defined(NANA_WINDOWS) ::Sleep(milliseconds); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct timespec timeOut, remains; timeOut.tv_sec = milliseconds / 1000; timeOut.tv_nsec = (milliseconds % 1000) * 1000000; @@ -56,6 +56,8 @@ namespace system return ::GetCurrentThreadId(); #elif defined(NANA_LINUX) return ::syscall(__NR_gettid); +#elif defined(NANA_MACOS) + return ::syscall(SYS_thread_selfid); #endif } @@ -63,7 +65,7 @@ namespace system { #if defined(NANA_WINDOWS) return ::GetTickCount(); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct timeval tv; ::gettimeofday(&tv, 0); return (tv.tv_sec * 1000 + tv.tv_usec / 1000); @@ -88,7 +90,7 @@ namespace system } return (::GetAsyncKeyState(button) != 0); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) return false; #endif } @@ -109,7 +111,7 @@ namespace system nana::detail::platform_spec::co_initializer co_init; ::ShellExecute(0, STR("open"), url.c_str(), 0, 0, SW_SHOWNORMAL); } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #endif } }//end namespace system diff --git a/source/system/shared_wrapper.cpp b/source/system/shared_wrapper.cpp index db208352..d545652e 100644 --- a/source/system/shared_wrapper.cpp +++ b/source/system/shared_wrapper.cpp @@ -12,7 +12,7 @@ #include #include #include -#ifdef NANA_LINUX +#if defined(NANA_LINUX) or defined(NANA_MACOS) #include #else #include @@ -30,7 +30,7 @@ namespace system module_t open(const char* filename) { -#ifdef NANA_LINUX +#if defined(NANA_LINUX) or defined(NANA_MACOS) return ::dlopen(filename, RTLD_LAZY); #else return ::LoadLibraryA(filename); @@ -39,7 +39,7 @@ namespace system void* symbols(module_t handle, const char* symbol) { -#ifdef NANA_LINUX +#if defined(NANA_LINUX) or defined(NANA_MACOS) return ::dlsym(handle, const_cast(symbol)); #else return (void*)(::GetProcAddress(reinterpret_cast(handle), symbol)); @@ -48,7 +48,7 @@ namespace system void close(module_t handle) { -#ifdef NANA_LINUX +#if defined(NANA_LINUX) or defined(NANA_MACOS) ::dlclose(handle); #else ::FreeLibrary(reinterpret_cast(handle)); @@ -90,7 +90,7 @@ namespace system std::transform(filename + length - 13, filename + length , std::back_inserter(file), tolower); if(file == ".nana_shared") { -#ifdef NANA_LINUX +#if defined(NANA_LINUX) or defined(NANA_MACOS) ofn.replace(length - 13, 13, ".so"); #else ofn.replace(length - 13, 13, ".DLL"); diff --git a/source/system/timepiece.cpp b/source/system/timepiece.cpp index 1c1f36fd..daae31dc 100644 --- a/source/system/timepiece.cpp +++ b/source/system/timepiece.cpp @@ -2,7 +2,7 @@ #include #ifdef NANA_WINDOWS #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #endif @@ -15,7 +15,7 @@ namespace system { #if defined(NANA_WINDOWS) LARGE_INTEGER beg_timestamp; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct timeval beg_timestamp; #endif }; @@ -45,7 +45,7 @@ namespace system { #if defined(NANA_WINDOWS) ::QueryPerformanceCounter(&impl_->beg_timestamp); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct timezone tz; ::gettimeofday(&impl_->beg_timestamp, &tz); #endif @@ -63,7 +63,7 @@ namespace system ::QueryPerformanceFrequency(&freq); return double(diff)/double(freq.QuadPart) * 1000; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); diff --git a/source/threads/pool.cpp b/source/threads/pool.cpp index 0676ba64..aa9bd461 100644 --- a/source/threads/pool.cpp +++ b/source/threads/pool.cpp @@ -27,7 +27,7 @@ #if defined(NANA_WINDOWS) #include #include -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) #include #endif @@ -61,7 +61,7 @@ namespace threads { #if defined(NANA_WINDOWS) typedef HANDLE thread_t; -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) typedef pthread_t thread_t; #endif impl * pool_ptr; @@ -69,7 +69,7 @@ namespace threads thread_t handle; volatile state thr_state; time_t timestamp; -#if defined(NANA_LINUX) +#if defined(NANA_LINUX) || defined(NANA_MACOS) std::mutex wait_mutex; std::condition_variable wait_cond; volatile bool suspended; @@ -89,7 +89,7 @@ namespace threads pto->task_ptr = nullptr; #if defined(NANA_WINDOWS) pto->handle = (HANDLE)::_beginthreadex(0, 0, reinterpret_cast(&impl::_m_thr_starter), pto, 0, 0); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) pto->suspended = false; ::pthread_create(&(pto->handle), 0, reinterpret_cast(&impl::_m_thr_starter), pto); #endif @@ -136,7 +136,7 @@ namespace threads #if defined(NANA_WINDOWS) ::WaitForSingleObject(thr->handle, INFINITE); ::CloseHandle(thr->handle); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) ::pthread_join(thr->handle, 0); ::pthread_detach(thr->handle); #endif @@ -222,7 +222,7 @@ namespace threads pto->thr_state = state::idle; #if defined(NANA_WINDOWS) ::SuspendThread(pto->handle); -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) std::unique_lock lock(pto->wait_mutex); pto->suspended = true; pto->wait_cond.wait(lock); @@ -239,7 +239,7 @@ namespace threads if(n == 1 || n == static_cast(-1)) break; } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) while(false == pto->suspended) ; std::unique_lock lock(pto->wait_mutex); @@ -326,7 +326,7 @@ namespace threads ::_endthreadex(0); return 0; } -#elif defined(NANA_LINUX) +#elif defined(NANA_LINUX) || defined(NANA_MACOS) static void * _m_thr_starter(pool_throbj * pto) { pto->pool_ptr->_m_thr_runner(pto);