diff options
author | Yaniv Kamay <ykamay@redhat.com> | 2009-09-19 21:25:46 +0300 |
---|---|---|
committer | Yaniv Kamay <ykamay@redhat.com> | 2009-10-14 15:06:41 +0200 |
commit | c1b79eb035fa158fb2ac3bc8e559809611070016 (patch) | |
tree | 3348dd749a700dedf87c9b16fe8be77c62928df8 /client/x11/platform.cpp |
fresh start
Diffstat (limited to 'client/x11/platform.cpp')
-rw-r--r-- | client/x11/platform.cpp | 2643 |
1 files changed, 2643 insertions, 0 deletions
diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp new file mode 100644 index 00000000..c99595a3 --- /dev/null +++ b/client/x11/platform.cpp @@ -0,0 +1,2643 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/XKBlib.h> +#include <X11/Xresource.h> +#include <X11/cursorfont.h> +#include <X11/extensions/Xrandr.h> +#include <X11/extensions/render.h> +#include <X11/extensions/XKB.h> +#include <X11/extensions/Xrender.h> +#include <unistd.h> +#include <sys/epoll.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <unistd.h> +#include <fcntl.h> +#include <set> +#include <values.h> +#include <signal.h> +#include <config.h> + +#include "platform.h" +#include "application.h" +#include "utils.h" +#include "x_platform.h" +#include "debug.h" +#include "monitor.h" +#include "rect.h" +#include "record.h" +#include "playback.h" +#include "resource.h" +#include "res.h" +#include "cursor.h" +#include "events_loop.h" + +#define DWORD uint32_t +#define BOOL bool +#include "named_pipe.h" + +//#define X_DEBUG_SYNC(display) XSync(display, False) +#define X_DEBUG_SYNC(display) +#ifdef HAVE_XRANDR12 +#define USE_XRANDR_1_2 +#endif + +static Display* x_display = NULL; +static XVisualInfo **vinfo = NULL; +static GLXFBConfig **fb_config; + +static EventsLoop events_loop; +static XContext win_proc_context; +static bool quit_request = false; +static pthread_t main_thread; +static int focus_count = 0; + +static bool using_xrandr_1_0 = false; +#ifdef USE_XRANDR_1_2 +static bool using_xrandr_1_2 = false; +#endif + +static int xrandr_event_base; +static int xrandr_error_base; +static int xrandr_major = 0; +static int xrandr_minor = 0; + +static bool using_xrender_0_5 = false; + +static unsigned int caps_lock_mask = 0; +static unsigned int num_lock_mask = 0; + +class DefaultEventListener: public Platform::EventListener { +public: + virtual void on_app_activated() {} + virtual void on_app_deactivated() {} + virtual void on_monitors_change() {} +}; + +static DefaultEventListener default_event_listener; +static Platform::EventListener* event_listener = &default_event_listener; + +class DefaultDisplayModeListner: public Platform::DisplayModeListner { +public: + void on_display_mode_change() {} +}; + +static DefaultDisplayModeListner default_display_mode_listener; +static Platform::DisplayModeListner* display_mode_listener = &default_display_mode_listener; + + +NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface) +{ + return (ListenerRef)(new LinuxListener(name, listener_interface, events_loop)); +} + +void NamedPipe::destroy(ListenerRef listener_ref) +{ + delete (LinuxListener *)listener_ref; +} + +void NamedPipe::destroy_connection(ConnectionRef conn_ref) +{ + delete (Session *)conn_ref; +} + +int32_t NamedPipe::read(ConnectionRef conn_ref, uint8_t* buf, int32_t size) +{ + if (((Session *)conn_ref) != NULL) { + return ((Session *)conn_ref)->read(buf, size); + } + return -1; +} + +int32_t NamedPipe::write(ConnectionRef conn_ref, const uint8_t* buf, int32_t size) +{ + if (((Session *)conn_ref) != NULL) { + return ((Session *)conn_ref)->write(buf, size); + } + return -1; +} + +class XEventHandler: public EventsLoop::File { +public: + XEventHandler(Display& x_display); + virtual void on_event() {} + virtual int get_fd() {return _x_fd;} + +private: + int _x_fd; +}; + +XEventHandler::XEventHandler(Display& x_display) +{ + if ((_x_fd = ConnectionNumber(&x_display)) == -1) { + THROW("get x fd failed"); + } +} + +class WakeupEventHandler: public EventsLoop::Trigger { +public: + virtual void on_event() {} +}; + +static WakeupEventHandler wakeup_handler; + +#define NSEC_PER_SEC (1000 * 1000 * 1000) + +class Timer { +public: + Timer(timer_proc_t proc, void* opaque); + ~Timer(); + + void arm(uint32_t msec); + void disarm(); + bool action(const timespec& now); + + const timespec& get_experatoin() const { return _experatoin;} + + static int get_timout(); + static void timers_action(); + +private: + void calc_next_experatoin_time(); + + static bool timespec_less(const timespec& time, const timespec& from); + static bool timespec_equal(const timespec& time, const timespec& from); + static bool timespec_less_or_equal(const timespec& time, const timespec& from); + +public: + static RecurciveMutex timers_lock; + +private: + timer_proc_t _proc; + void* _opaque; + bool _armed; + timespec _interval; + timespec _experatoin; + + class Compare { + public: + bool operator () (const Timer* timer1, const Timer* timer2) const + { + if (!Timer::timespec_less(timer1->get_experatoin(), timer2->get_experatoin())) { + return Timer::timespec_equal(timer1->get_experatoin(), timer2->get_experatoin()) ? + timer1 < timer2 : false; + } + return true; + } + }; + + typedef std::set<Timer*, Compare> TimersSet; + static TimersSet armed_timers; +}; + +Timer::TimersSet Timer::armed_timers; +RecurciveMutex Timer::timers_lock; + +Display* XPlatform::get_display() +{ + return x_display; +} + +XVisualInfo** XPlatform::get_vinfo() +{ + return vinfo; +} + +GLXFBConfig** XPlatform::get_fbconfig() +{ + return fb_config; +} + +void XPlatform::set_win_proc(Window win, win_proc_t proc) +{ + if (XSaveContext(x_display, win, win_proc_context, (XPointer)proc)) { + THROW("set win proc pailed"); + } +} + +void XPlatform::cleare_win_proc(Window win) +{ + XDeleteContext(x_display, win, win_proc_context); +} + +void Platform::send_quit_request() +{ + quit_request = true; + wakeup(); +} + +void Platform::wait_events() +{ + ASSERT(pthread_self() == main_thread); + XFlush(x_display); + if (!XPending(x_display)) { + events_loop.run_once(Timer::get_timout()); + Timer::timers_action(); + } +} + +void Platform::wakeup() +{ + wakeup_handler.trigger(); +} + +bool Platform::process_events() +{ + ASSERT(pthread_self() == main_thread); + while (XPending(x_display)) { + XPointer proc_pointer; + XEvent event; + + XNextEvent(x_display, &event); + if (event.xany.window == None) { + LOG_WARN("invalid window"); + continue; + } + + if (XFindContext(x_display, event.xany.window, win_proc_context, &proc_pointer)) { + THROW("no window proc"); + } + ((XPlatform::win_proc_t)proc_pointer)(event); + } + return quit_request; +} + +uint64_t Platform::get_monolithic_time() +{ + struct timespec time_space; + clock_gettime(CLOCK_MONOTONIC, &time_space); + return uint64_t(time_space.tv_sec) * 1000 * 1000 * 1000 + uint64_t(time_space.tv_nsec); +} + +void Platform::get_temp_dir(std::string& path) +{ + path = "/tmp/"; +} + +void Platform::msleep(unsigned int millisec) +{ + usleep(millisec * 1000); +} + +void Platform::yield() +{ + pthread_yield(); +} + +void Platform::set_thread_priority(void* thread, Platform::ThreadPriority in_priority) +{ + ASSERT(thread == NULL); + int priority; + + switch (in_priority) { + case PRIORITY_TIME_CRITICAL: + priority = -20; + break; + case PRIORITY_HIGH: + priority = -2; + break; + case PRIORITY_ABOVE_NORMAL: + priority = -1; + break; + case PRIORITY_NORMAL: + priority = 0; + break; + case PRIORITY_BELOW_NORMAL: + priority = 1; + break; + case PRIORITY_LOW: + priority = 2; + break; + case PRIORITY_IDLE: + priority = 19; + break; + default: + THROW("invalid priority %d", in_priority); + } + + pid_t tid = syscall(SYS_gettid); + if (setpriority(PRIO_PROCESS, tid, priority) == -1) { + DBG(0, "setpriority failed %s", strerror(errno)); + } +} + +Timer::Timer(timer_proc_t proc, void* opaque) + : _proc (proc) + , _opaque (opaque) + , _armed (false) +{ +} + +Timer::~Timer() +{ + disarm(); +} + +void Timer::arm(uint32_t msec) +{ + disarm(); + _interval.tv_sec = msec / 1000; + _interval.tv_nsec = (msec % 1000) * 1000 * 1000; + if (clock_gettime(CLOCK_MONOTONIC, &_experatoin)) { + THROW("gettime failed %s", strerror(errno)); + } + calc_next_experatoin_time(); + _armed = true; + armed_timers.insert(this); +} + +void Timer::disarm() +{ + if (!_armed) { + return; + } + armed_timers.erase(this); + _armed = false; +} + +#define TINER_COMPENSATION + +bool Timer::action(const timespec& now) +{ + ASSERT(_armed); + ASSERT(now.tv_nsec < NSEC_PER_SEC); + + if (timespec_less(now, _experatoin)) { + return false; + } + armed_timers.erase(this); +#ifndef TINER_COMPENSATION + _experatoin = now; +#endif + calc_next_experatoin_time(); +#ifdef TINER_COMPENSATION + if (timespec_less_or_equal(_experatoin, now)) { + _experatoin = now; + calc_next_experatoin_time(); + } +#endif + armed_timers.insert(this); + _proc(_opaque, (TimerID)this); + return true; +} + +int Timer::get_timout() +{ + RecurciveLock lock(Timer::timers_lock); + TimersSet::iterator iter; + iter = armed_timers.begin(); + if (iter == armed_timers.end()) { + return -1; + } + + timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now)) { + THROW("gettime failed %s", strerror(errno)); + } + + const timespec& next_time = (*iter)->get_experatoin(); + + if (!timespec_less(now, next_time)) { + return 0; + } + return ((next_time.tv_nsec - now.tv_nsec) / 1000 / 1000) + + (next_time.tv_sec - now.tv_sec) * 1000; +} + +void Timer::timers_action() +{ + RecurciveLock lock(timers_lock); + timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now)) { + THROW("gettime failed %s", strerror(errno)); + } + + TimersSet::iterator iter; + while ((iter = armed_timers.begin()) != armed_timers.end() && (*iter)->action(now)); +} + +void Timer::calc_next_experatoin_time() +{ + _experatoin.tv_nsec += _interval.tv_nsec; + _experatoin.tv_sec += (_experatoin.tv_nsec / NSEC_PER_SEC) + _interval.tv_sec; + _experatoin.tv_nsec %= NSEC_PER_SEC; +} + +bool Timer::timespec_less(const timespec& time, const timespec& from) +{ + return time.tv_sec < from.tv_sec || (time.tv_sec == from.tv_sec && time.tv_nsec < from.tv_nsec); +} + +bool Timer::timespec_equal(const timespec& time, const timespec& from) +{ + return time.tv_sec == from.tv_sec && time.tv_nsec <= from.tv_nsec; +} + +bool Timer::timespec_less_or_equal(const timespec& time, const timespec& from) +{ + return time.tv_sec < from.tv_sec || + (time.tv_sec == from.tv_sec && time.tv_nsec <= from.tv_nsec); +} + +TimerID Platform::create_interval_timer(timer_proc_t proc, void* opaque) +{ + return (TimerID) new Timer(proc, opaque); +} + +bool Platform::activate_interval_timer(TimerID timer, unsigned int millisec) +{ + RecurciveLock lock(Timer::timers_lock); + ((Timer*)timer)->arm(millisec); + if (pthread_self() != main_thread) { + wakeup(); + } + return true; +} + +bool Platform::deactivate_interval_timer(TimerID timer) +{ + RecurciveLock lock(Timer::timers_lock); + ((Timer*)timer)->disarm(); + return true; +} + +void Platform::destroy_interval_timer(TimerID timer) +{ + if (timer == INVALID_TIMER) { + return; + } + RecurciveLock lock(Timer::timers_lock); + delete (Timer*)timer; +} + +void Platform::set_event_listener(EventListener* listener) +{ + event_listener = listener ? listener : &default_event_listener; +} + +void Platform::set_display_mode_listner(DisplayModeListner* listener) +{ + display_mode_listener = listener ? listener : &default_display_mode_listener; +} + +#ifdef USE_XRANDR_1_2 +class FreeScreenResources { +public: + void operator () (XRRScreenResources* res) { XRRFreeScreenResources(res);} +}; +typedef _AutoRes<XRRScreenResources, FreeScreenResources> AutoScreenRes; + +class FreeOutputInfo { +public: + void operator () (XRROutputInfo* output_info) { XRRFreeOutputInfo(output_info);} +}; + +typedef _AutoRes<XRROutputInfo, FreeOutputInfo> AutoOutputInfo; + +class FreeCrtcInfo { +public: + void operator () (XRRCrtcInfo* crtc_info) { XRRFreeCrtcInfo(crtc_info);} +}; +typedef _AutoRes<XRRCrtcInfo, FreeCrtcInfo> AutoCrtcInfo; + +static XRRModeInfo* find_mod(XRRScreenResources* res, RRMode mode) +{ + for (int i = 0; i < res->nmode; i++) { + if (res->modes[i].id == mode) { + return &res->modes[i]; + } + } + return NULL; +} + +#endif + +//#define SHOW_SCREEN_INFO +#ifdef SHOW_SCREEN_INFO + +static float mode_refresh(XRRModeInfo *mode_info) +{ + if (!mode_info->hTotal || !mode_info->vTotal) { + return 0; + } + + return ((float)mode_info->dotClock / ((float)mode_info->hTotal * (float)mode_info->vTotal)); +} + +static void show_scren_info() +{ + int screen = DefaultScreen(x_display); + Window root_window = RootWindow(x_display, screen); + + int minWidth; + int minHeight; + int maxWidth; + int maxHeight; + + AutoScreenRes res(XRRGetScreenResources(x_display, root_window)); + + if (!res.valid()) { + throw Exception(fmt("%s: get screen resources failed") % __FUNCTION__); + } + + XRRGetScreenSizeRange(x_display, root_window, &minWidth, &minHeight, + &maxWidth, &maxHeight); + printf("screen: min %dx%d max %dx%d\n", minWidth, minHeight, + maxWidth, maxHeight); + + int i, j; + + for (i = 0; i < res->noutput; i++) { + AutoOutputInfo output_info(XRRGetOutputInfo(x_display, res.get(), res->outputs[i])); + + printf("output %s", output_info->name); + if (output_info->crtc == None) { + printf(" crtc None"); + } else { + printf(" crtc 0x%lx", output_info->crtc); + } + switch (output_info->connection) { + case RR_Connected: + printf(" Connected"); + break; + case RR_Disconnected: + printf(" Disconnected"); + break; + case RR_UnknownConnection: + printf(" UnknownConnection"); + break; + } + printf(" ncrtc %u nclone %u nmode %u\n", + output_info->ncrtc, + output_info->nclone, + output_info->nmode); + for (j = 0; j < output_info->nmode; j++) { + XRRModeInfo* mode = find_mod(res.get(), output_info->modes[j]); + printf("\t%lu:", output_info->modes[j]); + if (!mode) { + printf(" ???\n"); + continue; + } + printf(" %s %ux%u %f\n", mode->name, mode->width, mode->height, mode_refresh(mode)); + } + } + + for (i = 0; i < res->ncrtc; i++) { + AutoCrtcInfo crtc_info(XRRGetCrtcInfo(x_display, res.get(), res->crtcs[i])); + printf("crtc: 0x%lx x %d y %d width %u height %u mode %lu\n", + res->crtcs[i], + crtc_info->x, crtc_info->y, + crtc_info->width, crtc_info->height, crtc_info->mode); + } +} + +#endif + +enum RedScreenRotation { + RED_SCREEN_ROTATION_0, + RED_SCREEN_ROTATION_90, + RED_SCREEN_ROTATION_180, + RED_SCREEN_ROTATION_270, +}; + +enum RedSubpixelOrder { + RED_SUBPIXEL_ORDER_UNKNOWN, + RED_SUBPIXEL_ORDER_H_RGB, + RED_SUBPIXEL_ORDER_H_BGR, + RED_SUBPIXEL_ORDER_V_RGB, + RED_SUBPIXEL_ORDER_V_BGR, + RED_SUBPIXEL_ORDER_NONE, +}; + +static void root_win_proc(XEvent& event); +static void process_monitor_configure_events(Window root); + +class XMonitor; +typedef std::list<XMonitor*> XMonitorsList; + +class XScreen { +public: + XScreen(Display* display, int screen); + virtual ~XScreen() {} + + virtual void publish_monitors(MonitorsList& monitors) = 0; + + Display* get_display() {return _display;} + int get_screen() {return _screen;} + + void set_broken() {_broken = true;} + bool is_broken() const {return _broken;} + int get_width() const {return _width;} + void set_width(int width) {_width = width;} + int get_height() const { return _height;} + void set_height(int height) {_height = height;} + Point get_position() const {return _position;} + +private: + Display* _display; + int _screen; + Point _position; + int _width; + int _height; + bool _broken; +}; + +XScreen::XScreen(Display* display, int screen) + : _display (display) + , _screen (screen) + , _broken (false) +{ + int root = RootWindow(display, screen); + + XWindowAttributes attrib; + XGetWindowAttributes(display, root, &attrib); + + _position.x = attrib.x; + _position.y = attrib.y; + _width = attrib.width; + _height = attrib.height; +} + +class StaticScreen: public XScreen, public Monitor { +public: + StaticScreen(Display* display, int screen, int& next_mon_id) + : XScreen(display, screen) + , Monitor(next_mon_id++) + , _out_of_sync (false) + { + } + + virtual void publish_monitors(MonitorsList& monitors) + { + monitors.push_back(this); + } + + virtual void set_mode(int width, int height) + { + _out_of_sync = width > get_width() || height > get_height(); + } + + virtual void restore() {} + virtual int get_depth() { return XPlatform::get_vinfo()[0]->depth;} + virtual Point get_position() { return XScreen::get_position();} + virtual Point get_size() const { Point pt = {get_width(), get_height()}; return pt;} + virtual bool is_out_of_sync() { return _out_of_sync;} + virtual int get_screen_id() { return get_screen();} + +private: + bool _out_of_sync; +}; + +class DynamicScreen: public XScreen, public Monitor { +public: + DynamicScreen(Display* display, int screen, int& next_mon_id); + virtual ~DynamicScreen(); + + void publish_monitors(MonitorsList& monitors); + virtual void set_mode(int width, int height); + virtual void restore(); + virtual int get_depth() { return XPlatform::get_vinfo()[0]->depth;} + virtual Point get_position() { return XScreen::get_position();} + virtual Point get_size() const { Point pt = {get_width(), get_height()}; return pt;} + virtual bool is_out_of_sync() { return _out_of_sync;} + virtual int get_screen_id() { return get_screen();} + +private: + bool set_screen_size(int size_index); + +private: + int _saved_width; + int _saved_height; + bool _out_of_sync; +}; + +DynamicScreen::DynamicScreen(Display* display, int screen, int& next_mon_id) + : XScreen(display, screen) + , Monitor(next_mon_id++) + , _saved_width (get_width()) + , _saved_height (get_height()) + , _out_of_sync (false) +{ + X_DEBUG_SYNC(display); + Window root_window = RootWindow(display, screen); + XSelectInput(display, root_window, StructureNotifyMask); + XRRSelectInput(display, root_window, RRScreenChangeNotifyMask); + XPlatform::set_win_proc(root_window, root_win_proc); + X_DEBUG_SYNC(display); +} + +DynamicScreen::~DynamicScreen() +{ + restore(); +} + +void DynamicScreen::publish_monitors(MonitorsList& monitors) +{ + monitors.push_back(this); +} + +class SizeInfo { +public: + SizeInfo(int int_index, XRRScreenSize* in_size) : index (int_index), size (in_size) {} + + int index; + XRRScreenSize* size; +}; + +class SizeCompare { +public: + bool operator () (const SizeInfo& size1, const SizeInfo& size2) const + { + int area1 = size1.size->width * size1.size->height; + int area2 = size2.size->width * size2.size->height; + return area1 < area2 || (area1 == area2 && size1.index < size2.index); + } +}; + +void DynamicScreen::set_mode(int width, int height) +{ + int num_sizes; + + X_DEBUG_SYNC(get_display()); + XRRScreenSize* sizes = XRRSizes(get_display(), get_screen(), &num_sizes); + + typedef std::set<SizeInfo, SizeCompare> SizesSet; + SizesSet sizes_set; + + for (int i = 0; i < num_sizes; i++) { + if (sizes[i].width >= width && sizes[i].height >= height) { + sizes_set.insert(SizeInfo(i, &sizes[i])); + } + } + _out_of_sync = true; + if (!sizes_set.empty() && set_screen_size((*sizes_set.begin()).index)) { + _out_of_sync = false; + } + X_DEBUG_SYNC(get_display()); +} + +void DynamicScreen::restore() +{ + X_DEBUG_SYNC(get_display()); + if (is_broken() || (get_width() == _saved_width && get_height() == _saved_height)) { + return; + } + int num_sizes; + + XRRScreenSize* sizes = XRRSizes(get_display(), get_screen(), &num_sizes); + for (int i = 0; i < num_sizes; i++) { + if (sizes[i].width == _saved_width && sizes[i].height == _saved_height) { + set_screen_size(i); + return; + } + } + X_DEBUG_SYNC(get_display()); + LOG_WARN("can't find startup mode"); +} + +bool DynamicScreen::set_screen_size(int size_index) +{ + X_DEBUG_SYNC(get_display()); + Window root_window = RootWindow(get_display(), get_screen()); + XRRScreenConfiguration* config; + + if (!(config = XRRGetScreenInfo(get_display(), root_window))) { + LOG_WARN("get scren info failed"); + return false; + } + Rotation rotation; + XRRConfigCurrentConfiguration(config, &rotation); + + Monitor::self_monitors_change++; + /*what status*/ + XRRSetScreenConfig(get_display(), config, root_window, size_index, rotation, CurrentTime); + process_monitor_configure_events(root_window); + Monitor::self_monitors_change--; + XRRFreeScreenConfigInfo(config); + X_DEBUG_SYNC(get_display()); + + int num_sizes; + XRRScreenSize* sizes = XRRSizes(get_display(), get_screen(), &num_sizes); + if (num_sizes <= size_index) { + THROW("invalid sizes size"); + } + set_width(sizes[size_index].width); + set_height(sizes[size_index].height); + return true; +} + +#ifdef USE_XRANDR_1_2 + +class MultyMonScreen: public XScreen { +public: + MultyMonScreen(Display* display, int screen, int& next_mon_id); + virtual ~MultyMonScreen(); + + virtual void publish_monitors(MonitorsList& monitors); + + void disable(); + void enable(); + + bool set_monitor_mode(XMonitor& monitor, const XRRModeInfo& mode_info); + +private: + void set_size(int width, int height); + void get_trans_size(int& width, int& hight); + Point get_trans_top_left(); + Point get_trans_bottom_right(); + bool changed(); + + XMonitor* crtc_overlap_test(int x, int y, int width, int height); + void monitors_cleanup(); + void restore(); + +private: + int _min_width; + int _min_height; + int _max_width; + int _max_height; + int _saved_width; + int _saved_height; + int _saved_width_mm; + int _saved_height_mm; + XMonitorsList _monitors; +}; + +#define MAX_TRANS_DEPTH 3 + +class XMonitor: public Monitor { +public: + XMonitor(MultyMonScreen& container, int id, RRCrtc crtc); + virtual ~XMonitor(); + + virtual void set_mode(int width, int height); + virtual void restore(); + virtual int get_depth(); + virtual Point get_position(); + virtual Point get_size() const; + virtual bool is_out_of_sync(); + virtual int get_screen_id() { return _container.get_screen();} + + void add_clone(XMonitor *clone); + void revert(); + void disable(); + void enable(); + + void set_mode(const XRRModeInfo& mode); + const Rect& get_prev_area(); + Rect& get_trans_area(); + void pin() { _pin_count++;} + void unpin() { ASSERT(_pin_count > 0); _pin_count--;} + bool is_pinned() {return !!_pin_count;} + void commit_trans_position(); + void set_pusher(XMonitor& pusher) { _pusher = &pusher;} + XMonitor* get_pusher() { return _pusher;} + void push_trans(); + void begin_trans(); + bool mode_changed(); + bool position_changed(); + + static void inc_change_ref() { Monitor::self_monitors_change++;} + static void dec_change_ref() { Monitor::self_monitors_change--;} + +private: + void update_position(); + bool finde_mode_in_outputs(RRMode mode, int start_index, XRRScreenResources* res); + bool finde_mode_in_clones(RRMode mode, XRRScreenResources* res); + XRRModeInfo* find_mode(int width, int height, XRRScreenResources* res); + +private: + MultyMonScreen& _container; + RRCrtc _crtc; + XMonitorsList _clones; + Point _position; + Point _size; + RRMode _mode; + Rotation _rotation; + int _noutput; + RROutput* _outputs; + + Point _saved_position; + Point _saved_size; + RRMode _saved_mode; + Rotation _saved_rotation; + + bool _out_of_sync; + RedScreenRotation _red_rotation; + RedSubpixelOrder _subpixel_order; + + int _trans_depth; + Rect _trans_area[MAX_TRANS_DEPTH]; + int _pin_count; + XMonitor* _pusher; +}; + +MultyMonScreen::MultyMonScreen(Display* display, int screen, int& next_mon_id) + : XScreen(display, screen) + , _saved_width (get_width()) + , _saved_height (get_height()) + , _saved_width_mm (DisplayWidthMM(display, screen)) + , _saved_height_mm (DisplayHeightMM(display, screen)) +{ + X_DEBUG_SYNC(get_display()); + Window root_window = RootWindow(display, screen); + XRRGetScreenSizeRange(display, root_window, &_min_width, &_min_height, + &_max_width, &_max_height); + + AutoScreenRes res(XRRGetScreenResources(display, root_window)); + if (!res.valid()) { + THROW("get screen resources failed"); + } + +#ifdef SHOW_SCREEN_INFO + show_scren_info(); +#endif + try { + for (int i = 0; i < res->ncrtc; i++) { + AutoCrtcInfo crtc_info(XRRGetCrtcInfo(display, res.get(), res->crtcs[i])); + + if (!crtc_info.valid()) { + THROW("get crtc info failed"); + } + + if (crtc_info->mode == None) { + continue; + } + + ASSERT(crtc_info->noutput); + + XMonitor* clone_mon = crtc_overlap_test(crtc_info->x, crtc_info->y, + crtc_info->width, crtc_info->height); + + if (clone_mon) { + clone_mon->add_clone(new XMonitor(*this, next_mon_id++, res->crtcs[i])); + continue; + } + + _monitors.push_back(new XMonitor(*this, next_mon_id++, res->crtcs[i])); + } + } catch (...) { + monitors_cleanup(); + throw; + } + + XSelectInput(display, root_window, StructureNotifyMask); + XRRSelectInput(display, root_window, RRScreenChangeNotifyMask); + XPlatform::set_win_proc(root_window, root_win_proc); + X_DEBUG_SYNC(get_display()); +} + +MultyMonScreen::~MultyMonScreen() +{ + restore(); + monitors_cleanup(); +} + +XMonitor* MultyMonScreen::crtc_overlap_test(int x, int y, int width, int height) +{ + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + XMonitor* mon = *iter; + + Point pos = mon->get_position(); + Point size = mon->get_size(); + + if (x == pos.x && y == pos.y && width == size.x && height == size.y) { + return mon; + } + + if (x < pos.x + size.x && x + width > pos.x && y < pos.y + size.y && y + height > pos.y) { + THROW("unsupported partial overlap"); + } + } + return NULL; +} + +void MultyMonScreen::publish_monitors(MonitorsList& monitors) +{ + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + monitors.push_back(*iter); + } +} + +void MultyMonScreen::monitors_cleanup() +{ + while (!_monitors.empty()) { + XMonitor* monitor = _monitors.front(); + _monitors.pop_front(); + delete monitor; + } +} + +void MultyMonScreen::disable() +{ + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + (*iter)->disable(); + } +} + +void MultyMonScreen::enable() +{ + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + (*iter)->enable(); + } +} + +void MultyMonScreen::set_size(int width, int height) +{ + X_DEBUG_SYNC(get_display()); + Window root_window = RootWindow(get_display(), get_screen()); + set_width(width); + int width_mm = (int)((double)_saved_width_mm / _saved_width * width); + set_height(height); + int height_mm = (int)((double)_saved_height_mm / _saved_height * height); + XRRSetScreenSize(get_display(), root_window, width, height, width_mm, height_mm); + X_DEBUG_SYNC(get_display()); +} + +bool MultyMonScreen::changed() +{ + if (get_width() != _saved_width || get_height() != _saved_height) { + return true; + } + + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + if ((*iter)->mode_changed() || (*iter)->position_changed()) { + return true; + } + } + return false; +} + +void MultyMonScreen::restore() +{ + if (is_broken() || !changed()) { + return; + } + X_DEBUG_SYNC(get_display()); + XMonitor::inc_change_ref(); + disable(); + Window root_window = RootWindow(get_display(), get_screen()); + + XRRSetScreenSize(get_display(), root_window, _saved_width, + _saved_height, + _saved_width_mm, _saved_height_mm); + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + (*iter)->revert(); + } + enable(); + process_monitor_configure_events(root_window); + XMonitor::dec_change_ref(); + X_DEBUG_SYNC(get_display()); +} + +Point MultyMonScreen::get_trans_top_left() +{ + Point position; + position.y = position.x = MAXINT; + + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + Rect& area = (*iter)->get_trans_area(); + position.x = MIN(position.x, area.left); + position.y = MIN(position.y, area.top); + } + return position; +} + +Point MultyMonScreen::get_trans_bottom_right() +{ + Point position; + position.y = position.x = MININT; + + XMonitorsList::iterator iter = _monitors.begin(); + for (; iter != _monitors.end(); iter++) { + Rect& area = (*iter)->get_trans_area(); + position.x = MAX(position.x, area.right); + position.y = MAX(position.y, area.bottom); + } + return position; +} + +void MultyMonScreen::get_trans_size(int& width, int& height) +{ + ASSERT(get_trans_top_left().x == 0 && get_trans_top_left().y == 0); + Point bottom_right = get_trans_bottom_right(); + ASSERT(bottom_right.x > 0 && bottom_right.y > 0); + width = bottom_right.x; + height = bottom_right.y; +} + +#endif + +/*class Variant { + static void get_area_in_front(const Rect& base, int size, Rect& area) + static int get_push_distance(const Rect& fix_area, const Rect& other) + static int get_head(const Rect& area) + static int get_tail(const Rect& area) + static void move_head(Rect& area, int delta) + static int get_pull_distance(const Rect& fix_area, const Rect& other) + static void offset(Rect& area, int delta) + static void shrink(Rect& area, int delta) + static int get_distance(const Rect& area, const Rect& other_area) + static bool is_on_tail(const Rect& area, const Rect& other_area) + static bool is_on_perpendiculars(const Rect& area, const Rect& other_area) +}*/ + +#ifdef USE_XRANDR_1_2 + +class SortRightToLeft { +public: + bool operator () (XMonitor* mon1, XMonitor* mon2) const + { + return mon1->get_trans_area().right > mon2->get_trans_area().right; + } +}; + +typedef std::multiset<XMonitor*, SortRightToLeft> PushLeftSet; + +class LeftVariant { +public: + + static void get_area_in_front(const Rect& base, int size, Rect& area) + { + area.right = base.left; + area.left = area.right - size; + area.bottom = base.bottom; + area.top = base.top; + } + + static int get_push_distance(const Rect& fix_area, const Rect& other) + { + return other.right - fix_area.left; + } + + static int get_head(const Rect& area) + { + return area.left; + } + + static int get_tail(const Rect& area) + { + return area.right; + } + + static void move_head(Rect& area, int delta) + { + area.left -= delta; + ASSERT(area.right >= area.left); + } + + static int get_pull_distance(const Rect& fix_area, const Rect& other) + { + return other.left - fix_area.right; + } + + static void offset(Rect& area, int delta) + { + rect_offset(area, -delta, 0); + } + + static void shrink(Rect& area, int delta) + { + area.right -= delta; + ASSERT(area.right > area.left); + } + + static int get_distance(const Rect& area, const Rect& other_area) + { + return other_area.left - area.left; + } + + static bool is_on_tail(const Rect& area, const Rect& other_area) + { + return area.right == other_area.left && other_area.top < area.bottom && + other_area.bottom > area.top; + } + + static bool is_on_perpendiculars(const Rect& area, const Rect& other_area) + { + return (other_area.bottom == area.top || other_area.top == area.bottom) && + other_area.left < area.right && other_area.right > area.left; + } +}; + +class SortLeftToRight { +public: + bool operator () (XMonitor* mon1, XMonitor* mon2) const + { + return mon1->get_trans_area().left < mon2->get_trans_area().left; + } +}; + +typedef std::multiset<XMonitor*, SortLeftToRight> PushRightSet; + +class RightVariant { +public: + + static void get_area_in_front(const Rect& base, int size, Rect& area) + { + area.left = base.right; + area.right = area.left + size; + area.top = base.top; + area.bottom = base.bottom; + } + + static int get_push_distance(const Rect& fix_area, const Rect& other) + { + return fix_area.right - other.left; + } + + static int get_head(const Rect& area) + { + return area.right; + } + + static int get_tail(const Rect& area) + { + return area.left; + } + + static void move_head(Rect& area, int delta) + { + area.right += delta; + ASSERT(area.right >= area.left); + } + + static int get_pull_distance(const Rect& fix_area, const Rect& other) + { + return fix_area.left - other.right; + } + + static void offset(Rect& area, int delta) + { + rect_offset(area, delta, 0); + } + + static bool is_on_tail(const Rect& area, const Rect& other_area) + { + return other_area.right == area.left && other_area.top < area.bottom && + other_area.bottom > area.top; + } + + static bool is_on_perpendiculars(const Rect& area, const Rect& other_area) + { + return (other_area.bottom == area.top || other_area.top == area.bottom) && + other_area.left < area.right && other_area.right > area.left; + } +}; + +class SortBottomToTop { +public: + bool operator () (XMonitor* mon1, XMonitor* mon2) const + { + return mon1->get_trans_area().bottom > mon2->get_trans_area().bottom; + } +}; + +typedef std::multiset<XMonitor*, SortBottomToTop> PushTopSet; + +class TopVariant { +public: + static void get_area_in_front(const Rect& base, int size, Rect& area) + { + area.left = base.left; + area.right = base.right; + area.bottom = base.top; + area.top = area.bottom - size; + } + + static int get_push_distance(const Rect& fix_area, const Rect& other) + { + return other.bottom - fix_area.top; + } + + static int get_head(const Rect& area) + { + return area.top; + } + + static int get_tail(const Rect& area) + { + return area.bottom; + } + + static void move_head(Rect& area, int delta) + { + area.top -= delta; + ASSERT(area.bottom >= area.top); + } + + static int get_pull_distance(const Rect& fix_area, const Rect& other) + { + return other.top - fix_area.bottom; + } + + static void offset(Rect& area, int delta) + { + rect_offset(area, 0, -delta); + } + + static void shrink(Rect& area, int delta) + { + area.bottom -= delta; + ASSERT(area.bottom > area.top); + } + + static int get_distance(const Rect& area, const Rect& other_area) + { + return other_area.top - area.top; + } + + static bool is_on_tail(const Rect& area, const Rect& other_area) + { + return area.bottom == other_area.top && other_area.left < area.right && + other_area.right > area.left; + } + + static bool is_on_perpendiculars(const Rect& area, const Rect& other_area) + { + return (other_area.right == area.left || other_area.left == area.right) && + other_area.top < area.bottom && other_area.bottom > area.top; + } +}; + +class SortTopToBottom { +public: + bool operator () (XMonitor* mon1, XMonitor* mon2) const + { + return mon1->get_trans_area().top < mon2->get_trans_area().top; + } +}; + +typedef std::multiset<XMonitor*, SortTopToBottom> PushBottomSet; + +class BottomVariant { +public: + + static void get_area_in_front(const Rect& base, int size, Rect& area) + { + area.left = base.left; + area.right = base.right; + area.top = base.bottom; + area.bottom = area.top + size; + } + + static int get_push_distance(const Rect& fix_area, const Rect& other) + { + return fix_area.bottom - other.top; + } + + static int get_head(const Rect& area) + { + return area.bottom; + } + + static int get_tail(const Rect& area) + { + return area.top; + } + + static void move_head(Rect& area, int delta) + { + area.bottom += delta; + ASSERT(area.bottom >= area.top); + } + + static int get_pull_distance(const Rect& fix_area, const Rect& other) + { + return fix_area.top - other.bottom; + } + + static void offset(Rect& area, int delta) + { + rect_offset(area, 0, delta); + } + + static bool is_on_tail(const Rect& area, const Rect& other_area) + { + return other_area.bottom == area.top && other_area.left < area.right && + other_area.right > area.left; + } + + static bool is_on_perpendiculars(const Rect& area, const Rect& other_area) + { + return (other_area.right == area.left || other_area.left == area.right) && + other_area.top < area.bottom && other_area.bottom > area.top; + } +}; + +volatile int wait_for_me = false; + +template <class Variant> +static void bounce_back(XMonitor& monitor, const XMonitorsList& monitors, int head, int distance) +{ + ASSERT(distance > 0); + while (wait_for_me); + + for (XMonitorsList::const_iterator iter = monitors.begin(); iter != monitors.end(); iter++) { + Rect& area = (*iter)->get_trans_area(); + if (Variant::get_tail(area) == head && (*iter)->get_pusher() == &monitor) { + Variant::offset(area, -distance); + bounce_back<Variant>(**iter, monitors, Variant::get_head(area) + distance, distance); + //todo: pull_back monitors on perpendiculars + } + } +} + +template <class Variant, class SortList, class SortListIter> +static int push(XMonitor& pusher, XMonitor& monitor, const XMonitorsList& monitors, int delta) +{ + monitor.pin(); + monitor.set_pusher(pusher); + + SortList sort; + XMonitorsList::const_iterator iter = monitors.begin(); + for (; iter != monitors.end(); iter++) { + if (*iter == &monitor) { + continue; + } + sort.insert(*iter); + } + + Rect area_to_clear; + Variant::get_area_in_front(monitor.get_trans_area(), delta, area_to_clear); + + SortListIter sort_iter = sort.begin(); + + for (; sort_iter != sort.end(); sort_iter++) { + const Rect& other_area = (*sort_iter)->get_trans_area(); + + if (rect_intersects(area_to_clear, other_area)) { + int distance = Variant::get_push_distance(area_to_clear, other_area); + ASSERT(distance > 0); + if (!(*sort_iter)->is_pinned()) { + distance = distance - push<Variant, SortList, SortListIter>(monitor, **sort_iter, + monitors, distance); + } + + if (distance) { + delta -= distance; + bounce_back<Variant>(monitor, monitors, Variant::get_head(area_to_clear), distance); + Variant::move_head(area_to_clear, -distance); + } + } + } + Variant::offset(monitor.get_trans_area(), delta); + + const Rect& area = monitor.get_prev_area(); + for (iter = monitors.begin(); iter != monitors.end(); iter++) { + if ((*iter)->is_pinned()) { + continue; + } + + const Rect& other_area = (*iter)->get_prev_area(); + if (Variant::is_on_perpendiculars(area, other_area)) { + int current_distance = Variant::get_pull_distance(monitor.get_trans_area(), + (*iter)->get_trans_area()); + int base_distance = Variant::get_pull_distance(area, other_area); + int distance = current_distance - base_distance; + if (distance > 0) { + push<Variant, SortList, SortListIter>(monitor, **iter, monitors, distance); + } + } else if (Variant::is_on_tail(area, other_area)) { + int distance = Variant::get_pull_distance(monitor.get_trans_area(), + (*iter)->get_trans_area()); + ASSERT(distance >= 0); + push<Variant, SortList, SortListIter>(monitor, **iter, monitors, distance); + } + } + return delta; +} + +template <class Variant> +static void pin(XMonitor& monitor, const XMonitorsList& monitors) +{ + const Rect& area = monitor.get_trans_area(); + + for (XMonitorsList::const_iterator iter = monitors.begin(); iter != monitors.end(); iter++) { + const Rect& other_area = (*iter)->get_trans_area(); + if ((*iter)->is_pinned()) { + continue; + } + if (Variant::is_on_tail(other_area, area) || + Variant::is_on_perpendiculars(area, other_area)) { + (*iter)->pin(); + pin<Variant>(**iter, monitors); + } + } +} + +template <class Variant, class SortList, class SortListIter> +static void shrink(XMonitor& monitor, const XMonitorsList& monitors, int delta) +{ + monitor.pin(); + pin<Variant>(monitor, monitors); + ASSERT(delta > 0); + + SortList sort; + XMonitorsList::const_iterator iter = monitors.begin(); + for (; iter != monitors.end(); iter++) { + if (*iter == &monitor) { + continue; + } + sort.insert(*iter); + } + + const Rect area = monitor.get_trans_area(); + Variant::shrink(monitor.get_trans_area(), delta); + for (SortListIter sort_iter = sort.begin(); sort_iter != sort.end(); sort_iter++) { + const Rect& other_area = (*sort_iter)->get_trans_area(); + if (Variant::is_on_perpendiculars(area, other_area)) { + int distance = Variant::get_distance(area, other_area); + if (distance > 0) { + distance = MIN(distance, delta); + push<Variant, SortList, SortListIter>(monitor, **sort_iter, monitors, distance); + } + } else if (Variant::is_on_tail(area, other_area)) { + push<Variant, SortList, SortListIter>(monitor, **sort_iter, monitors, delta); + } + } +} + +template <class Variant, class SortList, class SortListIter> +static void expand(XMonitor& monitor, const XMonitorsList& monitors, int delta) +{ + monitor.pin(); + ASSERT(delta > 0); + + SortList sort; + XMonitorsList::const_iterator iter = monitors.begin(); + for (; iter != monitors.end(); iter++) { + if (*iter == &monitor) { + continue; + } + sort.insert(*iter); + } + + Rect area_to_clear; + Variant::get_area_in_front(monitor.get_trans_area(), delta, area_to_clear); + + for (SortListIter sort_iter = sort.begin(); sort_iter != sort.end(); sort_iter++) { + const Rect& other_area = (*sort_iter)->get_trans_area(); + + if (rect_intersects(area_to_clear, other_area)) { + int distance = Variant::get_push_distance(area_to_clear, other_area); + ASSERT(distance > 0); + ASSERT(!(*sort_iter)->is_pinned()); +#ifdef RED_DEBUG + int actual = +#endif + push<Variant, SortList, SortListIter>(monitor, **sort_iter, monitors, distance); + ASSERT(actual == distance); + } + } + Variant::move_head(monitor.get_trans_area(), delta); +} + +bool MultyMonScreen::set_monitor_mode(XMonitor& monitor, const XRRModeInfo& mode_info) +{ + if (is_broken()) { + return false; + } + + Point size = monitor.get_size(); + int dx = mode_info.width - size.x; + int dy = mode_info.height - size.y; + + XMonitorsList::iterator iter = _monitors.begin(); + + for (; iter != _monitors.end(); iter++) { + (*iter)->begin_trans(); + } + + if (dx > 0) { + expand<RightVariant, PushRightSet, PushRightSet::iterator>(monitor, _monitors, dx); + } else if (dx < 0) { + shrink<LeftVariant, PushLeftSet, PushLeftSet::iterator>(monitor, _monitors, -dx); + } + + for (iter = _monitors.begin(); iter != _monitors.end(); iter++) { + (*iter)->push_trans(); + } + + if (dy > 0) { + expand<BottomVariant, PushBottomSet, PushBottomSet::iterator>(monitor, _monitors, dy); + } else if (dy < 0) { + shrink<TopVariant, PushTopSet, PushTopSet::iterator>(monitor, _monitors, -dy); + } + + int screen_width; + int screen_height; + + get_trans_size(screen_width, screen_height); + + if (screen_width > _max_width || screen_height > _max_height) { + return false; + } + + screen_width = MAX(screen_width, _min_width); + screen_height = MAX(screen_height, _min_height); + + XMonitor::inc_change_ref(); + disable(); + for (iter = _monitors.begin(); iter != _monitors.end(); iter++) { + (*iter)->commit_trans_position(); + } + X_DEBUG_SYNC(get_display()); + monitor.set_mode(mode_info); + set_size(screen_width, screen_height); + enable(); + Window root_window = RootWindow(get_display(), get_screen()); + process_monitor_configure_events(root_window); + XMonitor::dec_change_ref(); + X_DEBUG_SYNC(get_display()); + return true; +} + +XMonitor::XMonitor(MultyMonScreen& container, int id, RRCrtc crtc) + : Monitor(id) + , _container (container) + , _crtc (crtc) + , _out_of_sync (false) +{ + update_position(); + _saved_position = _position; + _saved_size = _size; + _saved_mode = _mode; + _saved_rotation = _rotation; +} + +XMonitor::~XMonitor() +{ + while (!_clones.empty()) { + XMonitor* clone = _clones.front(); + _clones.pop_front(); + delete clone; + } + delete[] _outputs; +} + +void XMonitor::update_position() +{ + Display* display = _container.get_display(); + X_DEBUG_SYNC(display); + Window root_window = RootWindow(display, _container.get_screen()); + AutoScreenRes res(XRRGetScreenResources(display, root_window)); + + if (!res.valid()) { + THROW("get screen resources failed"); + } + + AutoCrtcInfo crtc_info(XRRGetCrtcInfo(display, res.get(), _crtc)); + + ASSERT(crtc_info->noutput); + + _position.x = crtc_info->x; + _position.y = crtc_info->y; + _size.x = crtc_info->width; + _size.y = crtc_info->height; + + switch (crtc_info->rotation & 0xf) { + case RR_Rotate_0: + _red_rotation = RED_SCREEN_ROTATION_0; + break; + case RR_Rotate_90: + _red_rotation = RED_SCREEN_ROTATION_90; + break; + case RR_Rotate_180: + _red_rotation = RED_SCREEN_ROTATION_180; + break; + case RR_Rotate_270: + _red_rotation = RED_SCREEN_ROTATION_270; + break; + default: + THROW("invalid rotation"); + } + + if (crtc_info->noutput > 1) { + _subpixel_order = RED_SUBPIXEL_ORDER_UNKNOWN; + return; + } + + AutoOutputInfo output_info(XRRGetOutputInfo(display, res.get(), crtc_info->outputs[0])); + + switch (output_info->subpixel_order) { + case SubPixelUnknown: + _subpixel_order = RED_SUBPIXEL_ORDER_UNKNOWN; + break; + case SubPixelHorizontalRGB: + _subpixel_order = RED_SUBPIXEL_ORDER_H_RGB; + break; + case SubPixelHorizontalBGR: + _subpixel_order = RED_SUBPIXEL_ORDER_H_BGR; + break; + case SubPixelVerticalRGB: + _subpixel_order = RED_SUBPIXEL_ORDER_V_RGB; + break; + case SubPixelVerticalBGR: + _subpixel_order = RED_SUBPIXEL_ORDER_V_BGR; + break; + case SubPixelNone: + _subpixel_order = RED_SUBPIXEL_ORDER_NONE; + break; + default: + THROW("invalid subpixel order"); + } + + _mode = crtc_info->mode; + _rotation = crtc_info->rotation; + _noutput = crtc_info->noutput; + _outputs = new RROutput[_noutput]; + memcpy(_outputs, crtc_info->outputs, _noutput * sizeof(RROutput)); + X_DEBUG_SYNC(display); +} + +bool XMonitor::finde_mode_in_outputs(RRMode mode, int start_index, XRRScreenResources* res) +{ + int i, j; + + X_DEBUG_SYNC(_container.get_display()); + for (i = start_index; i < _noutput; i++) { + AutoOutputInfo output_info(XRRGetOutputInfo(_container.get_display(), res, _outputs[i])); + for (j = 0; j < output_info->nmode; j++) { + if (output_info->modes[j] == mode) { + break; + } + } + if (j == output_info->nmode) { + return false; + } + } + X_DEBUG_SYNC(_container.get_display()); + return true; +} + +bool XMonitor::finde_mode_in_clones(RRMode mode, XRRScreenResources* res) +{ + XMonitorsList::iterator iter = _clones.begin(); + for (; iter != _clones.end(); iter++) { + if (!(*iter)->finde_mode_in_outputs(mode, 0, res)) { + return false; + } + } + return true; +} + +class ModeInfo { +public: + ModeInfo(int int_index, XRRModeInfo* in_info) : index (int_index), info (in_info) {} + + int index; + XRRModeInfo* info; +}; + +class ModeCompare { +public: + bool operator () (const ModeInfo& mode1, const ModeInfo& mode2) const + { + int area1 = mode1.info->width * mode1.info->height; + int area2 = mode2.info->width * mode2.info->height; + return area1 < area2 || (area1 == area2 && mode1.index < mode2.index); + } +}; + +XRRModeInfo* XMonitor::find_mode(int width, int height, XRRScreenResources* res) +{ + typedef std::set<ModeInfo, ModeCompare> ModesSet; + ModesSet modes_set; + X_DEBUG_SYNC(_container.get_display()); + AutoOutputInfo output_info(XRRGetOutputInfo(_container.get_display(), res, _outputs[0])); + for (int i = 0; i < output_info->nmode; i++) { + XRRModeInfo* mode_inf = find_mod(res, output_info->modes[i]); + if (mode_inf->width >= width && mode_inf->height >= height) { + modes_set.insert(ModeInfo(i, mode_inf)); + } + } + + while (!modes_set.empty()) { + ModesSet::iterator iter = modes_set.begin(); + + if (!finde_mode_in_outputs((*iter).info->id, 1, res)) { + modes_set.erase(iter); + continue; + } + + if (!finde_mode_in_clones((*iter).info->id, res)) { + modes_set.erase(iter); + continue; + } + return (*iter).info; + } + X_DEBUG_SYNC(_container.get_display()); + return NULL; +} + +void XMonitor::set_mode(int width, int height) +{ + if (width == _size.x && height == _size.y) { + _out_of_sync = false; + return; + } + Display* display = _container.get_display(); + X_DEBUG_SYNC(display); + Window root_window = RootWindow(display, _container.get_screen()); + AutoScreenRes res(XRRGetScreenResources(display, root_window)); + if (!res.valid()) { + THROW("get screen resource failed"); + } + XRRModeInfo* mode_info = find_mode(width, height, res.get()); + + if (!mode_info || !_container.set_monitor_mode(*this, *mode_info)) { + _out_of_sync = true; + X_DEBUG_SYNC(display); + return; + } + _out_of_sync = false; +} + +void XMonitor::revert() +{ + _position = _saved_position; + _size = _saved_size; + _mode = _saved_mode; + _rotation = _saved_rotation; + XMonitorsList::iterator iter = _clones.begin(); + for (; iter != _clones.end(); iter++) { + (*iter)->revert(); + } +} + +void XMonitor::disable() +{ + Display* display = _container.get_display(); + X_DEBUG_SYNC(display); + Window root_window = RootWindow(display, _container.get_screen()); + AutoScreenRes res(XRRGetScreenResources(display, root_window)); + if (!res.valid()) { + THROW("get screen resources failed"); + } + XRRSetCrtcConfig(display, res.get(), _crtc, CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + + XMonitorsList::iterator iter = _clones.begin(); + for (; iter != _clones.end(); iter++) { + (*iter)->disable(); + } + X_DEBUG_SYNC(display); +} + +void XMonitor::enable() +{ + Display* display = _container.get_display(); + X_DEBUG_SYNC(display); + Window root_window = RootWindow(display, _container.get_screen()); + AutoScreenRes res(XRRGetScreenResources(display, root_window)); + if (!res.valid()) { + THROW("get screen resources failed"); + } + XRRSetCrtcConfig(display, res.get(), _crtc, CurrentTime, + _position.x, _position.y, + _mode, _rotation, + _outputs, _noutput); + + XMonitorsList::iterator iter = _clones.begin(); + for (; iter != _clones.end(); iter++) { + (*iter)->enable(); + } + X_DEBUG_SYNC(display); +} + +bool XMonitor::mode_changed() +{ + return _size.x != _saved_size.x || _size.y != _saved_size.y || + _mode != _saved_mode || _rotation != _saved_rotation; +} + +bool XMonitor::position_changed() +{ + return _position.x != _saved_position.x || _position.y != _saved_position.y; +} + +void XMonitor::restore() +{ + if (!mode_changed()) { + return; + } + set_mode(_saved_size.x, _saved_size.y); +} + +int XMonitor::get_depth() +{ + return XPlatform::get_vinfo()[0]->depth; +} + +Point XMonitor::get_position() +{ + return _position; +} + +Point XMonitor::get_size() const +{ + return _size; +} + +bool XMonitor::is_out_of_sync() +{ + return _out_of_sync; +} + +void XMonitor::add_clone(XMonitor *clone) +{ + _clones.push_back(clone); +} + +const Rect& XMonitor::get_prev_area() +{ + return _trans_area[_trans_depth - 1]; +} + +Rect& XMonitor::get_trans_area() +{ + return _trans_area[_trans_depth]; +} + +void XMonitor::push_trans() +{ + _trans_depth++; + ASSERT(_trans_depth < MAX_TRANS_DEPTH); + _trans_area[_trans_depth] = _trans_area[_trans_depth - 1]; + _pin_count = 0; + _pusher = NULL; +} + +void XMonitor::begin_trans() +{ + _trans_area[0].left = _position.x; + _trans_area[0].right = _trans_area[0].left + _size.x; + _trans_area[0].top = _position.y; + _trans_area[0].bottom = _trans_area[0].top + _size.y; + _trans_area[1] = _trans_area[0]; + _trans_depth = 1; + _pin_count = 0; + _pusher = NULL; +} + +void XMonitor::commit_trans_position() +{ + _position.x = _trans_area[_trans_depth].left; + _position.y = _trans_area[_trans_depth].top; + XMonitorsList::iterator iter = _clones.begin(); + for (; iter != _clones.end(); iter++) { + (*iter)->_position = _position; + } +} + +void XMonitor::set_mode(const XRRModeInfo& mode) +{ + _mode = mode.id; + _size.x = mode.width; + _size.y = mode.height; + XMonitorsList::iterator iter = _clones.begin(); + for (; iter != _clones.end(); iter++) { + (*iter)->set_mode(mode); + } +} + +#endif + +static MonitorsList monitors; + +typedef std::list<XScreen*> ScreenList; +static ScreenList screens; + +const MonitorsList& Platform::init_monitors() +{ + int next_mon_id = 0; + ASSERT(screens.empty()); +#ifdef USE_XRANDR_1_2 + if (using_xrandr_1_2) { + for (int i = 0; i < ScreenCount(x_display); i++) { + screens.push_back(new MultyMonScreen(x_display, i, next_mon_id)); + } + } else +#endif + if (using_xrandr_1_0) { + for (int i = 0; i < ScreenCount(x_display); i++) { + screens.push_back(new DynamicScreen(x_display, i, next_mon_id)); + } + } else { + for (int i = 0; i < ScreenCount(x_display); i++) { + screens.push_back(new StaticScreen(x_display, i, next_mon_id)); + } + } + + ASSERT(monitors.empty()); + ScreenList::iterator iter = screens.begin(); + for (; iter != screens.end(); iter++) { + (*iter)->publish_monitors(monitors); + } + return monitors; +} + +void Platform::destroy_monitors() +{ + monitors.clear(); + while (!screens.empty()) { + XScreen* screen = screens.front(); + screens.pop_front(); + delete screen; + } +} + +bool Platform::is_monitors_pos_valid() +{ + return (ScreenCount(x_display) == 1); +} + +static void root_win_proc(XEvent& event) +{ +#ifdef USE_XRANDR_1_2 + ASSERT(using_xrandr_1_0 || using_xrandr_1_2); +#else + ASSERT(using_xrandr_1_0); +#endif + if (event.type == ConfigureNotify || event.type - xrandr_event_base == RRScreenChangeNotify) { + XRRUpdateConfiguration(&event); + if (event.type - xrandr_event_base == RRScreenChangeNotify) { + display_mode_listener->on_display_mode_change(); + } + + if (Monitor::is_self_change()) { + return; + } + + ScreenList::iterator iter = screens.begin(); + for (; iter != screens.end(); iter++) { + (*iter)->set_broken(); + } + event_listener->on_monitors_change(); + } + + /*switch (event.type - xrandr_event_base) { + case RRScreenChangeNotify: + //XRRScreenChangeNotifyEvent * = (XRRScreenChangeNotifyEvent *) &event; + XRRUpdateConfiguration(&event); + break; + }*/ +} + +static void process_monitor_configure_events(Window root) +{ + XSync(x_display, False); + XEvent event; + + while (XCheckTypedWindowEvent(x_display, root, ConfigureNotify, &event)) { + root_win_proc(event); + } + + while (XCheckTypedWindowEvent(x_display, root, xrandr_event_base + RRScreenChangeNotify, + &event)) { + root_win_proc(event); + } +} + +static void cleanup(void) +{ + int i; + + DBG(0, ""); + if (!Monitor::is_self_change()) { + Platform::destroy_monitors(); + } + if (vinfo) { + for (i = 0; i < ScreenCount(x_display); ++i) { + XFree(vinfo[i]); + } + delete vinfo; + vinfo = NULL; + } + if (fb_config) { + for (i = 0; i < ScreenCount(x_display); ++i) { + XFree(fb_config[i]); + } + delete fb_config; + fb_config = NULL; + } +} + +static void quit_handler(int sig) +{ + LOG_INFO("signal %d", sig); + Platform::send_quit_request(); +} + +static void abort_handler(int sig) +{ + LOG_INFO("signal %d", sig); + Platform::destroy_monitors(); +} + +static void init_xrandr() +{ + Bool xrandr_ext = XRRQueryExtension(x_display, &xrandr_event_base, &xrandr_error_base); + if (xrandr_ext) { + XRRQueryVersion(x_display, &xrandr_major, &xrandr_minor); + if (xrandr_major < 1) { + return; + } +#ifdef USE_XRANDR_1_2 + if (xrandr_major == 1 && xrandr_minor < 2) { + using_xrandr_1_0 = true; + return; + } + using_xrandr_1_2 = true; +#else + using_xrandr_1_0 = true; +#endif + } +} + +static void init_xrender() +{ + int event_base; + int error_base; + int major; + int minor; + + using_xrender_0_5 = XRenderQueryExtension(x_display, &event_base, &error_base) && + XRenderQueryVersion(x_display, &major, &minor) && (major > 0 || minor >= 5); +} + +unsigned int get_modifier_mask(KeySym modifier) +{ + int mask = 0; + int i; + + XModifierKeymap* map = XGetModifierMapping(x_display); + KeyCode keycode = XKeysymToKeycode(x_display, modifier); + if (keycode == NoSymbol) { + return 0; + } + + for (i = 0; i < 8; i++) { + if (map->modifiermap[map->max_keypermod * i] == keycode) { + mask = 1 << i; + } + } + XFreeModifiermap(map); + return mask; +} + +static void init_kbd() +{ + int xkb_major = XkbMajorVersion; + int xkb_minor = XkbMinorVersion; + int opcode; + int event; + int error; + + if (!XkbLibraryVersion(&xkb_major, &xkb_minor) || + !XkbQueryExtension(x_display, &opcode, &event, &error, &xkb_major, &xkb_minor)) { + return; + } + caps_lock_mask = get_modifier_mask(XK_Caps_Lock); + num_lock_mask = get_modifier_mask(XK_Num_Lock); +} + +static int x_error_handler(Display* display, XErrorEvent* error_event) +{ + char error_str[256]; + char request_str[256]; + char number_str[32]; + + char* display_name = XDisplayString(display); + XGetErrorText(display, error_event->error_code, error_str, sizeof(error_str)); + + if (error_event->request_code < 128) { + snprintf(number_str, sizeof(number_str), "%d", error_event->request_code); + XGetErrorDatabaseText(display, "XRequest", number_str, "", + request_str, sizeof(request_str)); + } else { + snprintf(request_str, sizeof(request_str), "%d", error_event->request_code); + } + + LOG_ERROR("x error on display %s error %s minor %u request %s", + display_name, + error_str, + (uint32_t)error_event->minor_code, + request_str); + exit(-1); + return 0; +} + +static int x_io_error_handler(Display* display) +{ + LOG_ERROR("x io error on %s", XDisplayString(display)); + exit(-1); + return 0; +} + +static XVisualInfo* get_x_vis_info(int screen) +{ + XVisualInfo vtemplate; + int count; + + Visual* visual = DefaultVisualOfScreen(ScreenOfDisplay(x_display, screen)); + vtemplate.screen = screen; + vtemplate.visualid = XVisualIDFromVisual(visual); + return XGetVisualInfo(x_display, VisualIDMask | VisualScreenMask, &vtemplate, &count); +} + +void Platform::init() +{ + int err, ev; + int threads_enable; + XEventHandler *x_event_handler; + + DBG(0, ""); + + threads_enable = XInitThreads(); + + main_thread = pthread_self(); + + if (!(x_display = XOpenDisplay(NULL))) { + THROW("open X display failed"); + } + + vinfo = new XVisualInfo *[ScreenCount(x_display)]; + memset(vinfo, 0, sizeof(XVisualInfo *) * ScreenCount(x_display)); + fb_config = new GLXFBConfig *[ScreenCount(x_display)]; + memset(fb_config, 0, sizeof(GLXFBConfig *) * ScreenCount(x_display)); + + // working with KDE and visual from glXGetVisualFromFBConfig is not working + // well. for now disabling OGL. + if (0 && threads_enable && glXQueryExtension(x_display, &err, &ev)) { + int num_configs; + int attrlist[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_STENCIL_SIZE, 4, + GLX_DEPTH_SIZE, 0, + None + }; + + for (int i = 0; i < ScreenCount(x_display); ++i) { + if (!(fb_config[i] = glXChooseFBConfig(x_display, i, attrlist, &num_configs))) { + vinfo[i] = get_x_vis_info(i); + } else { + ASSERT(num_configs > 0); + vinfo[i] = glXGetVisualFromFBConfig(x_display, fb_config[i][0]); + } + + if (!vinfo[i]) { + THROW("XGetVisualInfo failed"); + } + } + } else { + for (int i = 0; i < ScreenCount(x_display); ++i) { + if (!(vinfo[i] = get_x_vis_info(i))) { + THROW("XGetVisualInfo failed"); + } + } + } + + XSetErrorHandler(x_error_handler); + XSetIOErrorHandler(x_io_error_handler); + + win_proc_context = XUniqueContext(); + + init_kbd(); + init_xrandr(); + init_xrender(); + + x_event_handler = new XEventHandler(*x_display); + events_loop.add_file(*x_event_handler); + events_loop.add_trigger(wakeup_handler); + + struct sigaction act; + memset(&act, 0, sizeof(act)); + sigfillset(&act.sa_mask); + + act.sa_handler = quit_handler; + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + + act.sa_flags = SA_RESETHAND; + act.sa_handler = abort_handler; + sigaction(SIGABRT, &act, NULL); + sigaction(SIGILL, &act, NULL); + sigaction(SIGSEGV, &act, NULL); + sigaction(SIGFPE, &act, NULL); + + atexit(cleanup); +} + +uint32_t Platform::get_keyboard_modifiers() +{ + XKeyboardState keyboard_state; + uint32_t modifiers = 0; + + XGetKeyboardControl(x_display, &keyboard_state); + + if (keyboard_state.led_mask & 0x01) { + modifiers |= CAPS_LOCK_MODIFIER; + } + if (keyboard_state.led_mask & 0x02) { + modifiers |= NUM_LOCK_MODIFIER; + } + if (keyboard_state.led_mask & 0x04) { + modifiers |= SCROLL_LOCK_MODIFIER; + } + return modifiers; +} + +enum XLed { + X11_CAPS_LOCK_LED = 1, + X11_NUM_LOCK_LED, + X11_SCROLL_LOCK_LED, +}; + +static void set_keyboard_led(XLed led, int set) +{ + switch (led) { + case X11_CAPS_LOCK_LED: + if (caps_lock_mask) { + XkbLockModifiers(x_display, XkbUseCoreKbd, caps_lock_mask, set ? caps_lock_mask : 0); + } + return; + case X11_NUM_LOCK_LED: + if (num_lock_mask) { + XkbLockModifiers(x_display, XkbUseCoreKbd, num_lock_mask, set ? num_lock_mask : 0); + } + return; + case X11_SCROLL_LOCK_LED: + XKeyboardControl keyboard_control; + keyboard_control.led_mode = set ? LedModeOn : LedModeOff; + keyboard_control.led = led; + XChangeKeyboardControl(x_display, KBLed | KBLedMode, &keyboard_control); + return; + } +} + +void Platform::set_keyboard_modifiers(uint32_t modifiers) +{ + uint32_t now = get_keyboard_modifiers(); + + if ((now & CAPS_LOCK_MODIFIER) != (modifiers & CAPS_LOCK_MODIFIER)) { + set_keyboard_led(X11_CAPS_LOCK_LED, !!(modifiers & CAPS_LOCK_MODIFIER)); + } + if ((now & NUM_LOCK_MODIFIER) != (modifiers & NUM_LOCK_MODIFIER)) { + set_keyboard_led(X11_NUM_LOCK_LED, !!(modifiers & NUM_LOCK_MODIFIER)); + } + if ((now & SCROLL_LOCK_MODIFIER) != (modifiers & SCROLL_LOCK_MODIFIER)) { + set_keyboard_led(X11_SCROLL_LOCK_LED, !!(modifiers & SCROLL_LOCK_MODIFIER)); + } +} + +WaveRecordAbstract* Platform::create_recorder(RecordClinet& client, + uint32_t sampels_per_sec, + uint32_t bits_per_sample, + uint32_t channels) +{ + return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels); +} + +WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec, + uint32_t bits_per_sample, + uint32_t channels) +{ + return new WavePlayer(sampels_per_sec, bits_per_sample, channels); +} + +void XPlatform::on_focus_in() +{ + if (focus_count++ == 0) { + event_listener->on_app_activated(); + } +} + +void XPlatform::on_focus_out() +{ + ASSERT(focus_count > 0); + if (--focus_count == 0) { + event_listener->on_app_deactivated(); + } +} + +class XBaseLocalCursor: public LocalCursor { +public: + XBaseLocalCursor() : _handle (0) {} + ~XBaseLocalCursor(); + void set(Window window); + +protected: + Cursor _handle; +}; + +void XBaseLocalCursor::set(Window window) +{ + if (_handle) { + XDefineCursor(x_display, window, _handle); + } +} + +XBaseLocalCursor::~XBaseLocalCursor() +{ + if (_handle) { + XFreeCursor(x_display, _handle); + } +} + +class XLocalCursor: public XBaseLocalCursor { +public: + XLocalCursor(CursorData* cursor_data); +}; + +static inline uint8_t get_pix_mask(const uint8_t* data, int offset, int pix_index) +{ + return data[offset + (pix_index >> 3)] & (0x80 >> (pix_index % 8)); +} + +static inline uint32_t get_pix_hack(int pix_index, int width) +{ + return (((pix_index % width) ^ (pix_index / width)) & 1) ? 0xc0303030 : 0x30505050; +} + +XLocalCursor::XLocalCursor(CursorData* cursor_data) +{ + const CursorHeader& header = cursor_data->header(); + const uint8_t* data = cursor_data->data(); + int cur_size = header.width * header.height; + uint8_t pix_mask; + uint32_t pix; + uint16_t i; + int size; + + if (!get_size_bits(header, size)) { + THROW("invalid curosr type"); + } + + uint32_t* cur_data = new uint32_t[cur_size]; + + switch (header.type) { + case CURSOR_TYPE_ALPHA: + break; + case CURSOR_TYPE_COLOR32: + memcpy(cur_data, data, cur_size * sizeof(uint32_t)); + for (i = 0; i < cur_size; i++) { + pix_mask = get_pix_mask(data, size, i); + if (pix_mask && *((uint32_t*)data + i) == 0xffffff) { + cur_data[i] = get_pix_hack(i, header.width); + } else { + cur_data[i] |= (pix_mask ? 0 : 0xff000000); + } + } + break; + case CURSOR_TYPE_COLOR16: + for (i = 0; i < cur_size; i++) { + pix_mask = get_pix_mask(data, size, i); + pix = *((uint16_t*)data + i); + if (pix_mask && pix == 0x7fff) { + cur_data[i] = get_pix_hack(i, header.width); + } else { + cur_data[i] = ((pix & 0x1f) << 3) | ((pix & 0x3e0) << 6) | ((pix & 0x7c00) << 9) | + (pix_mask ? 0 : 0xff000000); + } + } + break; + case CURSOR_TYPE_MONO: + for (i = 0; i < cur_size; i++) { + pix_mask = get_pix_mask(data, 0, i); + pix = get_pix_mask(data, size, i); + if (pix_mask && pix) { + cur_data[i] = get_pix_hack(i, header.width); + } else { + cur_data[i] = (pix ? 0xffffff : 0) | (pix_mask ? 0 : 0xff000000); + } + } + break; + case CURSOR_TYPE_COLOR4: + for (i = 0; i < cur_size; i++) { + pix_mask = get_pix_mask(data, size + (sizeof(uint32_t) << 4), i); + int idx = (i & 1) ? (data[i >> 1] & 0x0f) : ((data[i >> 1] & 0xf0) >> 4); + pix = *((uint32_t*)(data + size) + idx); + if (pix_mask && pix == 0xffffff) { + cur_data[i] = get_pix_hack(i, header.width); + } else { + cur_data[i] = pix | (pix_mask ? 0 : 0xff000000); + } + } + break; + case CURSOR_TYPE_COLOR24: + case CURSOR_TYPE_COLOR8: + default: + LOG_WARN("unsupported cursor type %d", header.type); + _handle = XCreateFontCursor(x_display, XC_arrow); + delete[] cur_data; + return; + } + + XImage image; + memset(&image, 0, sizeof(image)); + image.width = header.width; + image.height = header.height; + image.data = (header.type == CURSOR_TYPE_ALPHA ? (char*)data : (char*)cur_data); + image.byte_order = LSBFirst; + image.bitmap_unit = 32; + image.bitmap_bit_order = LSBFirst; + image.bitmap_pad = 8; + image.bytes_per_line = header.width << 2; + image.depth = 32; + image.format = ZPixmap; + image.bits_per_pixel = 32; + image.red_mask = 0x00ff0000; + image.green_mask = 0x0000ff00; + image.blue_mask = 0x000000ff; + if (!XInitImage(&image)) { + THROW("init image failed"); + } + + Window root_window = RootWindow(x_display, DefaultScreen(x_display)); + Pixmap pixmap = XCreatePixmap(x_display, root_window, header.width, header.height, 32); + + XGCValues gc_vals; + gc_vals.function = GXcopy; + gc_vals.foreground = ~0; + gc_vals.background = 0; + gc_vals.plane_mask = AllPlanes; + + GC gc = XCreateGC(x_display, pixmap, GCFunction | GCForeground | GCBackground | GCPlaneMask, + &gc_vals); + XPutImage(x_display, pixmap, gc, &image, 0, 0, 0, 0, header.width, header.height); + XFreeGC(x_display, gc); + + XRenderPictFormat *xformat = XRenderFindStandardFormat(x_display, PictStandardARGB32); + Picture picture = XRenderCreatePicture(x_display, pixmap, xformat, 0, NULL); + _handle = XRenderCreateCursor(x_display, picture, header.hot_spot_x, header.hot_spot_y); + XRenderFreePicture(x_display, picture); + XFreePixmap(x_display, pixmap); + delete[] cur_data; +} + +LocalCursor* Platform::create_local_cursor(CursorData* cursor_data) +{ + ASSERT(using_xrender_0_5); + return new XLocalCursor(cursor_data); +} + +class XInactiveCursor: public XBaseLocalCursor { +public: + XInactiveCursor() { _handle = XCreateFontCursor(x_display, XC_X_cursor);} +}; + +LocalCursor* Platform::create_inactive_cursor() +{ + return new XInactiveCursor(); +} + +class XDefaultCursor: public XBaseLocalCursor { +public: + XDefaultCursor() { _handle = XCreateFontCursor(x_display, XC_top_left_arrow);} +}; + +LocalCursor* Platform::create_default_cursor() +{ + return new XDefaultCursor(); +} + |