summaryrefslogtreecommitdiffstats
path: root/client/x11/platform.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/x11/platform.cpp')
-rw-r--r--client/x11/platform.cpp2643
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();
+}
+