mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-14 22:47:12 +00:00
Remove app/xvfb and lib/xev_track
The xvfb support remained unused for years. So let's remove it.
This commit is contained in:
parent
3268a16d13
commit
7f5e2c2eb2
repos/os
lib/mk
src
app/xvfb
lib/xev_track
test/xev_track
@ -1,5 +0,0 @@
|
||||
SRC_CC = xev_track.cc
|
||||
REQUIRES = x11 xdamage
|
||||
LIBS = lx_hybrid
|
||||
|
||||
vpath xev_track.cc $(REP_DIR)/src/lib/xev_track
|
@ -1,48 +0,0 @@
|
||||
Xvfb is a virtual X server that uses a plain file as framebuffer instead of a
|
||||
physical screen. The 'xvfb' glue program makes an Xvfb session available to the
|
||||
Linux version of Genode such that both native Genode programs and X clients can
|
||||
run seamlessly integrated in one Nitpicker session. Using the 'xvfb' glue
|
||||
program contained in this directory. Because Xvfb is executed as Nitpicker
|
||||
client, it is possible to integrate multiple instances of Xvfb into the same
|
||||
Nitpicker session.
|
||||
|
||||
|
||||
Preconditions for compiling
|
||||
---------------------------
|
||||
|
||||
The 'xvfb' glue program is a hybrid process using the Genode interfaces and the
|
||||
Linux interfaces directly. It tracks dirty screen regions using the X damage
|
||||
extension and injects user-input events into the X server using the X test
|
||||
extension. So you need the development package of both extensions to compile
|
||||
it. The Debian package for the X damage extension is called 'libxdamage-dev'.
|
||||
The X test extension is normally installed by default. Furthermore you need to
|
||||
enhance your 'SPECS' declaration in your
|
||||
'<builddir>/etc/specs.conf' file as follows:
|
||||
|
||||
! SPECS += x11 xdamage xtest
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
First start Xvfb using the following command-line arguments:
|
||||
|
||||
! Xvfb :1 -fbdir /tmp -screen 0 1024x768x16
|
||||
|
||||
While Xvfb is running, '/tmp/Xvfb_screen0' will contain the content of the X
|
||||
server's frame buffer. This file must be specified for the 'xvfb' declaration
|
||||
in the config file. In addition, the display of X server instance must be
|
||||
declared via the 'display' tag. For example:
|
||||
|
||||
! <config>
|
||||
! <display>:1</display>
|
||||
! <xvfb>/tmp/Xvfb_screen0</xvfb>
|
||||
! </config>
|
||||
|
||||
|
||||
Known Limitations
|
||||
-----------------
|
||||
|
||||
* With the current version, some keycodes are not mapped correctly.
|
||||
* The screen mode of Nitpicker and the Xvfb session must be the same.
|
||||
Only modes with 16bit color depth are supported.
|
@ -1,99 +0,0 @@
|
||||
/*
|
||||
* \brief Inject input event into an X server
|
||||
* \author Norman Feske
|
||||
* \date 2009-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include "inject_input.h"
|
||||
|
||||
/* Genode includes */
|
||||
#include <input/keycodes.h>
|
||||
#include <base/printf.h>
|
||||
|
||||
/* X11 includes */
|
||||
#include <X11/extensions/XTest.h>
|
||||
|
||||
|
||||
static int convert_keycode_to_x11[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
|
||||
80, 81, 82, 83, 43, 85, 86, 87, 88, 115, 119, 120, 121, 375, 123, 90,
|
||||
284, 285, 309, 298, 312, 91, 327, 328, 329, 331, 333, 335, 336, 337, 338, 339,
|
||||
367, 294, 293, 286, 350, 92, 334, 512, 116, 377, 109, 111, 373, 347, 348, 349,
|
||||
360, 93, 94, 95, 98, 376, 100, 101, 357, 316, 354, 304, 289, 102, 351, 355,
|
||||
103, 104, 105, 275, 281, 272, 306, 106, 274, 107, 288, 364, 358, 363, 362, 361,
|
||||
291, 108, 381, 290, 287, 292, 279, 305, 280, 99, 112, 257, 258, 113, 270, 114,
|
||||
118, 117, 125, 374, 379, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
|
||||
271, 273, 276, 277, 278, 282, 283, 295, 296, 297, 299, 300, 301, 302, 303, 307,
|
||||
308, 310, 313, 314, 315, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 330,
|
||||
332, 340, 341, 342, 343, 344, 345, 346, 356, 359, 365, 368, 369, 370, 371, 372
|
||||
};
|
||||
|
||||
|
||||
static int convert_keycode(int keycode)
|
||||
{
|
||||
if (keycode < 0 || keycode >= (int)(sizeof(convert_keycode_to_x11)/sizeof(int)))
|
||||
return 0;
|
||||
|
||||
return convert_keycode_to_x11[keycode] + 8;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
** Public functions **
|
||||
**********************/
|
||||
|
||||
bool inject_input_init(Display *dpy)
|
||||
{
|
||||
int dummy;
|
||||
if (!XTestQueryExtension (dpy, &dummy, &dummy, &dummy, &dummy)) {
|
||||
Genode::printf ("Error: Could not query Xtest extension\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void inject_input_event(Display *dpy, Input::Event &ev)
|
||||
{
|
||||
switch (ev.type()) {
|
||||
|
||||
case Input::Event::MOTION:
|
||||
XTestFakeMotionEvent(dpy, -1, ev.ax(), ev.ay(), CurrentTime);
|
||||
break;
|
||||
|
||||
case Input::Event::PRESS:
|
||||
if (ev.code() == Input::BTN_LEFT)
|
||||
XTestFakeButtonEvent(dpy, 1, 1, CurrentTime);
|
||||
if (ev.code() == Input::BTN_RIGHT)
|
||||
XTestFakeButtonEvent(dpy, 2, 1, CurrentTime);
|
||||
else
|
||||
XTestFakeKeyEvent(dpy, convert_keycode(ev.code()), 1, CurrentTime);
|
||||
break;
|
||||
|
||||
case Input::Event::RELEASE:
|
||||
if (ev.code() == Input::BTN_LEFT)
|
||||
XTestFakeButtonEvent(dpy, 1, 0, CurrentTime);
|
||||
if (ev.code() == Input::BTN_RIGHT)
|
||||
XTestFakeButtonEvent(dpy, 2, 0, CurrentTime);
|
||||
else
|
||||
XTestFakeKeyEvent(dpy, convert_keycode(ev.code()), 0, CurrentTime);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* flush faked input events */
|
||||
XFlush(dpy);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* \brief Interface for X input injection
|
||||
* \author Norman Feske
|
||||
* \date 2009-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INJECT_INPUT_H_
|
||||
#define _INJECT_INPUT_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <input/event.h>
|
||||
|
||||
/* X11 includes */
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
|
||||
/**
|
||||
* Initialize X input-injection mechanism
|
||||
*
|
||||
* \return true on success
|
||||
*/
|
||||
bool inject_input_init(Display *dpy);
|
||||
|
||||
|
||||
/**
|
||||
* Inject input event to the X session
|
||||
*/
|
||||
void inject_input_event(Display *dpy, Input::Event &ev);
|
||||
|
||||
|
||||
#endif /* _INJECT_INPUT_H_ */
|
@ -1,348 +0,0 @@
|
||||
/*
|
||||
* \brief Xvfb display application for Nitpicker
|
||||
* \author Norman Feske
|
||||
* \date 2009-11-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Linux includes */
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* X11 includes */
|
||||
#include <X11/XWDFile.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <nitpicker_session/connection.h>
|
||||
#include <nitpicker_view/client.h>
|
||||
#include <framebuffer_session/client.h>
|
||||
#include <input_session/client.h>
|
||||
#include <input/event.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <blit/blit.h>
|
||||
#include <xev_track/xev_track.h>
|
||||
#include <os/config.h>
|
||||
|
||||
/* local includes */
|
||||
#include "inject_input.h"
|
||||
|
||||
|
||||
typedef Genode::uint16_t Pixel;
|
||||
|
||||
|
||||
/**
|
||||
* Variables defined by the supplied config file
|
||||
*/
|
||||
char *config_xvfb_file_name = 0;
|
||||
char *config_x_display = 0;
|
||||
int config_force_top = 1;
|
||||
|
||||
/**
|
||||
* Variables derived from the xvfb screen
|
||||
*/
|
||||
static int xvfb_width, xvfb_height;
|
||||
static Pixel *xvfb_addr;
|
||||
|
||||
static Framebuffer::Session *fb_session;
|
||||
static Framebuffer::Mode fb_mode;
|
||||
static Pixel *fb_addr;
|
||||
|
||||
|
||||
static Nitpicker::Session *nitpicker()
|
||||
{
|
||||
struct Connection : Nitpicker::Connection
|
||||
{
|
||||
Connection()
|
||||
{
|
||||
Nitpicker::Connection::buffer(mode(), false);
|
||||
}
|
||||
};
|
||||
|
||||
static Connection connection;
|
||||
return &connection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse configuration
|
||||
*
|
||||
* \return true if configuration is complete
|
||||
*/
|
||||
static bool read_config()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
Xml_node config_node = config()->xml_node();
|
||||
|
||||
try {
|
||||
static char buf[256];
|
||||
config_node.sub_node("xvfb").value(buf, sizeof(buf));
|
||||
config_xvfb_file_name = buf;
|
||||
} catch (Xml_node::Nonexistent_sub_node) {
|
||||
printf("Error: Declaration of xvfb file name is missing\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
static char buf[256];
|
||||
config_node.sub_node("display").value(buf, sizeof(buf));
|
||||
config_x_display = buf;
|
||||
} catch (Xml_node::Nonexistent_sub_node) {
|
||||
printf("Error: Display declaration is missing\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline long convert_from_big_endian(long x)
|
||||
{
|
||||
char v[4] = {
|
||||
(char)((x & 0xff000000) >> 24),
|
||||
(char)((x & 0x00ff0000) >> 16),
|
||||
(char)((x & 0x0000ff00) >> 8),
|
||||
(char)((x & 0x000000ff) >> 0),
|
||||
};
|
||||
return *(long *)v;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map file (read only) to local address space
|
||||
*/
|
||||
static void *mmap_file(const char *file_name)
|
||||
{
|
||||
int fd = open(file_name, O_RDONLY);
|
||||
if (fd < 0)
|
||||
Genode::printf("Error: Could not open file %s\n", file_name);
|
||||
|
||||
struct stat stat;
|
||||
if (fstat(fd, &stat) < 0)
|
||||
Genode::printf("Error: Could not fstat file %s\n", file_name);
|
||||
|
||||
void *addr = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (addr == (void *)-1)
|
||||
Genode::printf("Error: Could not mmap file\n");
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flush part of the Xvfb screen to the Nitpicker session
|
||||
*/
|
||||
static void flush(int x, int y, int width, int height, Framebuffer::Mode const &mode)
|
||||
{
|
||||
/* clip arguments against framebuffer geometry */
|
||||
if (x < 0) width += x, x = 0;
|
||||
if (y < 0) height += y, y = 0;
|
||||
if (width > mode.width() - x) width = mode.width() - x;
|
||||
if (height > mode.height() - y) height = mode.height() - y;
|
||||
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
/* copy pixels from xvfb to the nitpicker buffer */
|
||||
blit(xvfb_addr + x + xvfb_width*y, xvfb_width*sizeof(Pixel),
|
||||
fb_addr + x + mode.width()*y, mode.width()*sizeof(Pixel),
|
||||
width*sizeof(Pixel), height);
|
||||
|
||||
/* refresh nitpicker views */
|
||||
fb_session->refresh(x, y, width, height);
|
||||
}
|
||||
|
||||
|
||||
class Bounding_box
|
||||
{
|
||||
private:
|
||||
|
||||
int _x1, _y1, _x2, _y2;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Bounding_box() { reset(); }
|
||||
|
||||
bool valid() { return _x1 <= _x2 && _y1 <= _y2; }
|
||||
|
||||
void reset() { _x1 = 0, _y1 = 0, _x2 = -1, _y2 = -1; }
|
||||
|
||||
void extend(int x, int y, int w, int h)
|
||||
{
|
||||
int nx2 = x + w - 1;
|
||||
int ny2 = y + h - 1;
|
||||
|
||||
if (!valid()) {
|
||||
_x1 = x, _y1 = y, _x2 = x + w - 1, _y2 = y + h - 1;
|
||||
} else {
|
||||
_x1 = Genode::min(_x1, x);
|
||||
_y1 = Genode::min(_y1, y);
|
||||
_x2 = Genode::max(_x2, nx2);
|
||||
_y2 = Genode::max(_y2, ny2);
|
||||
}
|
||||
}
|
||||
|
||||
int x() { return _x1; }
|
||||
int y() { return _y1; }
|
||||
int w() { return _x2 - _x1 + 1; }
|
||||
int h() { return _y2 - _y1 + 1; }
|
||||
};
|
||||
|
||||
static Bounding_box pending_redraw;
|
||||
|
||||
|
||||
/**************************************************
|
||||
** Hook functions called by the X event tracker **
|
||||
**************************************************/
|
||||
|
||||
struct View
|
||||
{
|
||||
Nitpicker::View_capability cap;
|
||||
};
|
||||
|
||||
static View views[MAX_VIEWS];
|
||||
|
||||
|
||||
void create_view(int view_id)
|
||||
{
|
||||
views[view_id].cap = nitpicker()->create_view();
|
||||
}
|
||||
|
||||
|
||||
void destroy_view(int view_id)
|
||||
{
|
||||
nitpicker()->destroy_view(views[view_id].cap);
|
||||
|
||||
/* invalidate capability */
|
||||
views[view_id].cap = Nitpicker::View_capability();
|
||||
}
|
||||
|
||||
|
||||
void set_background_view(int view_id)
|
||||
{
|
||||
nitpicker()->background(views[view_id].cap);
|
||||
}
|
||||
|
||||
|
||||
void place_view(int view_id, int x, int y, int w, int h)
|
||||
{
|
||||
Nitpicker::View_client view(views[view_id].cap);
|
||||
view.viewport(x, y, w, h, -x, -y, false);
|
||||
}
|
||||
|
||||
|
||||
void stack_view(int view_id, int neighbor_id, bool behind)
|
||||
{
|
||||
Nitpicker::View_client view(views[view_id].cap);
|
||||
|
||||
/* translate invalid neighbor ID to invalid capability */
|
||||
Nitpicker::View_capability neighbor_cap;
|
||||
if (neighbor_id >= 0)
|
||||
neighbor_cap = views[neighbor_id].cap;
|
||||
|
||||
view.stack(neighbor_cap, behind, true);
|
||||
}
|
||||
|
||||
|
||||
void refresh(int x, int y, int w, int h)
|
||||
{
|
||||
pending_redraw.extend(x, y, w, h);
|
||||
}
|
||||
|
||||
|
||||
int main(int, char **)
|
||||
{
|
||||
if (!read_config()) return -1;
|
||||
|
||||
static Timer::Connection timer;
|
||||
|
||||
static Framebuffer::Session_client fb(nitpicker()->framebuffer_session());
|
||||
static Input::Session_client input(nitpicker()->input_session());
|
||||
fb_session = &fb;
|
||||
fb_addr = Genode::env()->rm_session()->attach(fb.dataspace());
|
||||
fb_mode = fb.mode();
|
||||
|
||||
XWDFileHeader *xwd = (XWDFileHeader *)mmap_file(config_xvfb_file_name);
|
||||
if (!xwd) return -1;
|
||||
|
||||
xvfb_width = convert_from_big_endian(xwd->pixmap_width);
|
||||
xvfb_height = convert_from_big_endian(xwd->pixmap_height);
|
||||
|
||||
enum { SUPPORTED_BPP = 16 };
|
||||
if (convert_from_big_endian(xwd->bits_per_pixel) != SUPPORTED_BPP) {
|
||||
Genode::printf("Error: Color depth %d is not supported (use %d bpp)\n",
|
||||
(int)convert_from_big_endian(xwd->pixmap_depth), SUPPORTED_BPP);
|
||||
return -2;
|
||||
}
|
||||
|
||||
xvfb_addr = (Pixel *)((long)xwd + convert_from_big_endian(xwd->header_size)
|
||||
+ convert_from_big_endian(xwd->ncolors)*sizeof(XWDColor));
|
||||
|
||||
if (xvfb_width != fb_mode.width() || xvfb_height != fb_mode.height()) {
|
||||
Genode::printf("Error: Xvfb mode must equal the Nitpicker screen mode of %dx%d\n",
|
||||
fb_mode.width(), fb_mode.height());
|
||||
return -3;
|
||||
}
|
||||
|
||||
Input::Event *ev_buf = Genode::env()->rm_session()->attach(input.dataspace());
|
||||
|
||||
Display *dpy = XOpenDisplay(config_x_display);
|
||||
if (!dpy) {
|
||||
Genode::printf("Error: Cannot open display\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (!inject_input_init(dpy))
|
||||
return -5;
|
||||
|
||||
if (!xev_track_init(dpy))
|
||||
return -6;
|
||||
|
||||
for (;;) {
|
||||
pending_redraw.reset();
|
||||
|
||||
/*
|
||||
* Process due X events and update 'pending_redraw' as side effect
|
||||
*/
|
||||
XEvent ev;
|
||||
while (XPending(dpy)) {
|
||||
XNextEvent(dpy, &ev);
|
||||
xev_track_handle_event(dpy, ev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forward input events to the X session
|
||||
*/
|
||||
while (input.pending()) {
|
||||
|
||||
int num_ev = input.flush();
|
||||
for (int i = 0; i < num_ev; i++)
|
||||
inject_input_event(dpy, ev_buf[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Track mouse cursor and update 'pending_redraw' as side effect
|
||||
*/
|
||||
xev_track_handle_cursor(dpy);
|
||||
|
||||
if (pending_redraw.valid())
|
||||
flush(pending_redraw.x(), pending_redraw.y(),
|
||||
pending_redraw.w(), pending_redraw.h(), fb_mode);
|
||||
|
||||
timer.msleep(5);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
TARGET = xvfb
|
||||
REQUIRES = linux x11 xtest xdamage
|
||||
SRC_CC = main.cc inject_input.cc
|
||||
LIBS = lx_hybrid syscall-linux blit xev_track config
|
||||
|
||||
EXT_OBJECTS += -lX11 -lXdamage /usr/lib/libXtst.so.6
|
@ -1,529 +0,0 @@
|
||||
/*
|
||||
* \brief Window-event tracker for the X Window System
|
||||
* \author Norman Feske
|
||||
* \date 2009-11-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Linux includes */
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* X11 includes */
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
|
||||
/* xev includes */
|
||||
#include <xev_track/xev_track.h>
|
||||
|
||||
|
||||
struct View
|
||||
{
|
||||
bool tracked; /* flag indicating that the view is in use */
|
||||
Window xwin;
|
||||
Window above;
|
||||
int x, y, w, h, border;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* Make sure that the window is initially marked as free
|
||||
*/
|
||||
View() : tracked(false) { }
|
||||
};
|
||||
|
||||
|
||||
static View views[MAX_VIEWS];
|
||||
static Window root;
|
||||
static int xdamage_ev;
|
||||
static Damage damage;
|
||||
|
||||
|
||||
/***************
|
||||
** Utilities **
|
||||
***************/
|
||||
|
||||
/**
|
||||
* Allocate view ID
|
||||
*
|
||||
* \return allocated view ID, or
|
||||
* -1 if allocation failed
|
||||
*/
|
||||
static int alloc_view_id()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_VIEWS; i++)
|
||||
if (!views[i].tracked) break;
|
||||
|
||||
if (i == MAX_VIEWS)
|
||||
return -1;
|
||||
|
||||
views[i].tracked = true;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mark view ID as free
|
||||
*/
|
||||
static void release_view_id(int view_id)
|
||||
{
|
||||
views[view_id].tracked = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find view ID for a given X window ID
|
||||
*/
|
||||
static int find_view_id(Window xwin)
|
||||
{
|
||||
/* search for window with matchin xwin id */
|
||||
for (int i = 0; i < MAX_VIEWS; i++)
|
||||
if (views[i].xwin == xwin && views[i].tracked)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create new window
|
||||
*
|
||||
* \param position -1 .. at top,
|
||||
* -2 .. background,
|
||||
* otherwise window id of the neighbor behind the new
|
||||
* window
|
||||
*/
|
||||
static void assign_window(int view_id, Window xwin, Display *dpy, int position)
|
||||
{
|
||||
/* sanity check */
|
||||
if (view_id < 0) return;
|
||||
|
||||
View *view = &views[view_id];
|
||||
|
||||
view->xwin = xwin;
|
||||
|
||||
/* request position and size of new window */
|
||||
XWindowAttributes attr;
|
||||
XGetWindowAttributes(dpy, xwin, &attr);
|
||||
view->x = attr.x;
|
||||
view->y = attr.y;
|
||||
view->w = attr.width;
|
||||
view->h = attr.height;
|
||||
view->border = attr.border_width;
|
||||
|
||||
create_view(view_id);
|
||||
|
||||
if (position == -2) {
|
||||
stack_view(view_id, -1, false);
|
||||
set_background_view(view_id);
|
||||
}
|
||||
|
||||
if (position == -1)
|
||||
stack_view(view_id, -1, true);
|
||||
|
||||
if (position >= 0)
|
||||
stack_view(view_id, position, true);
|
||||
|
||||
place_view(view_id, view->x, view->y, view->w + view->border*2,
|
||||
view->h + view->border*2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan all currently present windows
|
||||
*/
|
||||
static void scan_windows(Display *dpy, Window root)
|
||||
{
|
||||
Window dummy_w1, dummy_w2, *win_list;
|
||||
unsigned int num_wins;
|
||||
XQueryTree(dpy, root, &dummy_w1, &dummy_w2, &win_list, &num_wins);
|
||||
|
||||
for (unsigned i = 0; i < num_wins; i++) {
|
||||
|
||||
XWindowAttributes attr;
|
||||
XGetWindowAttributes(dpy, win_list[i], &attr);
|
||||
|
||||
if (attr.map_state != IsViewable)
|
||||
continue;
|
||||
|
||||
int view_id = alloc_view_id();
|
||||
if (view_id >= 0)
|
||||
assign_window(view_id, win_list[i], dpy, -1);
|
||||
}
|
||||
XFree(win_list);
|
||||
|
||||
/* listen at the root window */
|
||||
XSelectInput(dpy, root, SubstructureNotifyMask | PointerMotionMask);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find view belonging to the window in front of the specified X window
|
||||
*
|
||||
* \return view ID, or
|
||||
* -1 if no matching view exists
|
||||
*/
|
||||
static int find_view_in_front(Display *dpy, Window root, Window win)
|
||||
{
|
||||
Window dummy_w1, dummy_w2, *win_list;
|
||||
unsigned int num_wins, i;
|
||||
|
||||
XQueryTree(dpy, root, &dummy_w1, &dummy_w2, &win_list, &num_wins);
|
||||
|
||||
/* find window in X window stack */
|
||||
for (i = 0; i < num_wins; i++)
|
||||
if (win_list[i] == win)
|
||||
break;
|
||||
|
||||
/* skip current window */
|
||||
i++;
|
||||
|
||||
/* find and return view belonging to the X window */
|
||||
int curr;
|
||||
for (; i < num_wins; i++)
|
||||
if ((curr = find_view_id(win_list[i])))
|
||||
return curr;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void get_pointer_pos(Display *dpy, int *mx, int *my)
|
||||
{
|
||||
Window dummy_win;
|
||||
int dummy_int;
|
||||
unsigned dummy_uint;
|
||||
XQueryPointer(dpy, root, &dummy_win, &dummy_win,
|
||||
mx, my, &dummy_int, &dummy_int, &dummy_uint);
|
||||
}
|
||||
|
||||
|
||||
struct Mouse_cursor_tracker
|
||||
{
|
||||
private:
|
||||
|
||||
enum { CURSOR_WIDTH = 20, CURSOR_HEIGHT = 20 };
|
||||
int _x1, _y1, _x2, _y2;
|
||||
bool _valid;
|
||||
|
||||
public:
|
||||
|
||||
Mouse_cursor_tracker() : _valid(false) { }
|
||||
|
||||
void reset(int x, int y)
|
||||
{
|
||||
_x1 = x - CURSOR_WIDTH;
|
||||
_y1 = y - CURSOR_HEIGHT;
|
||||
_x2 = x + CURSOR_WIDTH;
|
||||
_y2 = y + CURSOR_HEIGHT;
|
||||
_valid = false;
|
||||
}
|
||||
|
||||
void track(int x, int y)
|
||||
{
|
||||
if (_x1 > x - CURSOR_WIDTH) _x1 = x - CURSOR_WIDTH;
|
||||
if (_y1 > y - CURSOR_HEIGHT) _y1 = y - CURSOR_HEIGHT;
|
||||
if (_x2 < x + CURSOR_WIDTH) _x2 = x + CURSOR_WIDTH;
|
||||
if (_y2 < y + CURSOR_HEIGHT) _y2 = y + CURSOR_HEIGHT;
|
||||
_valid = true;
|
||||
}
|
||||
|
||||
bool bounding_box(int *x, int *y, int *w, int *h)
|
||||
{
|
||||
*x = _x1;
|
||||
*y = _y1;
|
||||
*w = _x2 - _x1 + 1;
|
||||
*h = _y2 - _y1 + 1;
|
||||
return _valid;
|
||||
}
|
||||
};
|
||||
|
||||
struct Mouse_cursor_tracker mouse_cursor_tracker;
|
||||
|
||||
|
||||
/****************************
|
||||
** Top-window enforcement **
|
||||
****************************/
|
||||
|
||||
/*
|
||||
* Some window managers do not raise a window that is already on top. This is
|
||||
* bad because there may be overlay windows that are not known to the X window
|
||||
* system but that cover the topmost X window. Thus, we want always to receive
|
||||
* a top event. For this, we create a dedicated invisible window that stays
|
||||
* always on top of all others. The topmost real X window is always the second.
|
||||
* Therefore, the window manager thinks that this window can be topped and
|
||||
* generates the desired event.
|
||||
*/
|
||||
static Window topwin;
|
||||
|
||||
|
||||
static bool window_left_of_screen(Display *dpy, Window xwin)
|
||||
{
|
||||
XWindowAttributes attr;
|
||||
XGetWindowAttributes(dpy, xwin, &attr);
|
||||
|
||||
return (attr.x + attr.width <= 0);
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
MAGIC_WIN_X = 2000, MAGIC_WIN_Y = 2000,
|
||||
MAGIC_WIN_W = 1, MAGIC_WIN_H = 1
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create magic window that stays always on top
|
||||
*/
|
||||
static void create_magic_topwin(Display *dpy, Window root)
|
||||
{
|
||||
XWindowChanges wincfg;
|
||||
|
||||
/* create magic window that stays always on top */
|
||||
topwin = XCreateWindow(dpy, root,
|
||||
MAGIC_WIN_X, MAGIC_WIN_Y, /* position */
|
||||
MAGIC_WIN_W, MAGIC_WIN_H, /* size */
|
||||
0, /* border */
|
||||
CopyFromParent, /* depth */
|
||||
InputOutput, /* class */
|
||||
CopyFromParent, /* visual */
|
||||
0, 0);
|
||||
|
||||
wincfg.x = MAGIC_WIN_X;
|
||||
wincfg.y = MAGIC_WIN_Y;
|
||||
XConfigureWindow(dpy, topwin, CWX | CWY , &wincfg);
|
||||
XMapWindow(dpy, topwin);
|
||||
wincfg.x = MAGIC_WIN_X;
|
||||
wincfg.y = MAGIC_WIN_Y;
|
||||
XConfigureWindow(dpy, topwin, CWX | CWY , &wincfg);
|
||||
|
||||
|
||||
int view_id = alloc_view_id();
|
||||
if (view_id >= 0)
|
||||
assign_window(view_id, topwin, dpy, root);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bring magic window in front of all others
|
||||
*/
|
||||
static void raise_magic_window(Display *dpy)
|
||||
{
|
||||
XRaiseWindow(dpy, topwin);
|
||||
|
||||
/*
|
||||
* Some window managers tend to relocate existing windows on startup. So
|
||||
* let's re-position the window to make sure that it remains invisible in
|
||||
* such cases.
|
||||
*/
|
||||
XMoveWindow(dpy, topwin, -200, -200);
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
** X event handling **
|
||||
**********************/
|
||||
|
||||
static void handle_xwindow_event(Display *dpy, Window root, XEvent &ev)
|
||||
{
|
||||
int view_id, behind_id;
|
||||
int x, y, w, h;
|
||||
View *view;
|
||||
|
||||
switch (ev.type) {
|
||||
|
||||
case MotionNotify:
|
||||
|
||||
x = ev.xmotion.x_root;
|
||||
y = ev.xmotion.y_root;
|
||||
mouse_cursor_tracker.track(x, y);
|
||||
break;
|
||||
|
||||
case ConfigureNotify:
|
||||
|
||||
if ((view_id = find_view_id(ev.xconfigure.window)) < 0)
|
||||
break;
|
||||
|
||||
x = ev.xconfigure.x;
|
||||
y = ev.xconfigure.y;
|
||||
w = ev.xconfigure.width;
|
||||
h = ev.xconfigure.height;
|
||||
|
||||
view = &views[view_id];
|
||||
|
||||
/*
|
||||
* If window position and size keeps the same,
|
||||
* we assume, the window has been topped.
|
||||
*/
|
||||
if (x == view->x && y == view->y && w == view->w && h == view->h) {
|
||||
|
||||
int behind_id = find_view_in_front(dpy, root, ev.xconfigure.window);
|
||||
|
||||
stack_view(view_id, behind_id, true);
|
||||
|
||||
if (!window_left_of_screen(dpy, ev.xconfigure.window) && config_force_top)
|
||||
raise_magic_window(dpy);
|
||||
|
||||
} else {
|
||||
|
||||
/* keep track new window position */
|
||||
view->x = x; view->y = y; view->w = w; view->h = h;
|
||||
|
||||
place_view(view_id, x, y, w + view->border*2, h + view->border*2);
|
||||
}
|
||||
break;
|
||||
|
||||
case Expose:
|
||||
|
||||
if ((view_id = find_view_id(ev.xconfigure.window)) < 0)
|
||||
break;
|
||||
|
||||
stack_view(view_id, -1, true);
|
||||
break;
|
||||
|
||||
case UnmapNotify:
|
||||
|
||||
if ((view_id = find_view_id(ev.xconfigure.window)) < 0)
|
||||
break;
|
||||
|
||||
/* avoid destroying a view twice */
|
||||
if (!views[view_id].tracked)
|
||||
break;
|
||||
|
||||
destroy_view(view_id);
|
||||
release_view_id(view_id);
|
||||
break;
|
||||
|
||||
case MapNotify:
|
||||
|
||||
if ((view_id = find_view_id(ev.xconfigure.window)) >= 0) {
|
||||
printf("MapRequest: window already present - view ID %d\n", view_id);
|
||||
break;
|
||||
}
|
||||
|
||||
behind_id = find_view_in_front(dpy, root, ev.xconfigure.window);
|
||||
|
||||
/*
|
||||
* Idea: Call XQueryTree to obtain the position where the new
|
||||
* window is located in the window stack.
|
||||
*
|
||||
*/
|
||||
|
||||
view_id = alloc_view_id();
|
||||
if (view_id >= 0)
|
||||
assign_window(view_id, ev.xconfigure.window, dpy, behind_id);
|
||||
|
||||
if (!window_left_of_screen(dpy, ev.xconfigure.window) && config_force_top)
|
||||
raise_magic_window(dpy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void handle_xdamage_event(Display *dpy, XEvent &ev)
|
||||
{
|
||||
if (ev.type != XDamageNotify + xdamage_ev)
|
||||
return;
|
||||
|
||||
static XserverRegion region = XFixesCreateRegion (dpy, 0, 0);
|
||||
static XserverRegion part = XFixesCreateRegion (dpy, 0, 0);
|
||||
|
||||
XDamageNotifyEvent *dev = (XDamageNotifyEvent *)&ev;
|
||||
|
||||
XFixesSetRegion(dpy, part, &dev->area, 1);
|
||||
XFixesUnionRegion(dpy, region, region, part);
|
||||
XFlush(dpy);
|
||||
|
||||
XRectangle *rects;
|
||||
int nrects;
|
||||
|
||||
nrects = 0;
|
||||
rects = XFixesFetchRegion (dpy, region, &nrects);
|
||||
|
||||
for (int i = 0; i < nrects; i++)
|
||||
refresh(rects[i].x, rects[i].y, rects[i].width, rects[i].height);
|
||||
|
||||
/* clear collected damage region from damage */
|
||||
XDamageSubtract (dpy, damage, region, None);
|
||||
|
||||
/* empty collected region */
|
||||
XFixesSetRegion (dpy, region, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Error handler that is called on xlib errors
|
||||
*/
|
||||
static int x_error_handler(Display *dpy, XErrorEvent *r)
|
||||
{
|
||||
printf("Error: x_error_handler called\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
** Public interface **
|
||||
**********************/
|
||||
|
||||
void xev_track_handle_event(Display *dpy, XEvent &ev)
|
||||
{
|
||||
handle_xwindow_event(dpy, root, ev);
|
||||
handle_xdamage_event(dpy, ev);
|
||||
}
|
||||
|
||||
|
||||
void xev_track_handle_cursor(Display *dpy)
|
||||
{
|
||||
int x = 0, y = 0, w = 0, h = 0;
|
||||
static int old_mx, old_my;
|
||||
int new_mx, new_my;
|
||||
|
||||
get_pointer_pos(dpy, &new_mx, &new_my);
|
||||
if (new_mx != old_mx || new_my != old_my)
|
||||
mouse_cursor_tracker.track(new_mx, new_my);
|
||||
|
||||
if (mouse_cursor_tracker.bounding_box(&x, &y, &w, &h))
|
||||
refresh(x, y, w, h);
|
||||
|
||||
mouse_cursor_tracker.reset(old_mx, old_my);
|
||||
mouse_cursor_tracker.track(new_mx, new_my);
|
||||
|
||||
old_mx = new_mx, old_my = new_my;
|
||||
}
|
||||
|
||||
|
||||
bool xev_track_init(Display *dpy)
|
||||
{
|
||||
XSetErrorHandler(x_error_handler);
|
||||
|
||||
int screen = DefaultScreen(dpy);
|
||||
root = RootWindow(dpy, screen);
|
||||
|
||||
int error;
|
||||
if (!XDamageQueryExtension (dpy, &xdamage_ev, &error)) {
|
||||
printf("Error: Could not query Xdamage extension\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
damage = XDamageCreate(dpy, root, XDamageReportBoundingBox);
|
||||
|
||||
if (config_force_top)
|
||||
create_magic_topwin(dpy, root);
|
||||
|
||||
/* create background view */
|
||||
int bg_view_id = alloc_view_id();
|
||||
if (bg_view_id >= 0)
|
||||
assign_window(bg_view_id, root, dpy, -2);
|
||||
|
||||
/* retrieve information about currently present windows */
|
||||
scan_windows(dpy, root);
|
||||
|
||||
return true;
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* \brief Test for X event tracker, dumping X11 events
|
||||
* \author Norman Feske
|
||||
* \date 2010-02-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* X11 includes */
|
||||
#include <X11/XWDFile.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
/* X event-tracker includes */
|
||||
#include <xev_track/xev_track.h>
|
||||
|
||||
/* standard includes */
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/*****************************
|
||||
** configuration variables **
|
||||
*****************************/
|
||||
|
||||
int config_force_top = 1; /* evaluated by the X event-tracker library */
|
||||
|
||||
static int config_dump_refresh = false;
|
||||
|
||||
|
||||
|
||||
/*******************************
|
||||
** X event-tracker callbacks **
|
||||
*******************************/
|
||||
|
||||
void create_view(int view_id) {
|
||||
printf("create_view(view_id=%d)\n", view_id); }
|
||||
|
||||
|
||||
void destroy_view(int view_id) {
|
||||
printf("destroy_view(view_id=%d)\n", view_id); }
|
||||
|
||||
|
||||
void set_background_view(int view_id) {
|
||||
printf("set_background_view(view_id=%d)\n", view_id); }
|
||||
|
||||
|
||||
void place_view(int view_id, int x, int y, int w, int h) {
|
||||
printf("place_view(view_id=%d, x=%d, y=%d, w=%d, h=%d)\n",
|
||||
view_id, x, y, w, h); }
|
||||
|
||||
|
||||
void stack_view(int view_id, int neighbor_id, bool behind) {
|
||||
printf("stack_view(view_id=%d, neighbor_id=%d, behind=%d)\n",
|
||||
view_id, neighbor_id, behind); }
|
||||
|
||||
|
||||
void refresh(int x, int y, int w, int h) {
|
||||
if (config_dump_refresh)
|
||||
printf("refresh(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h); }
|
||||
|
||||
|
||||
/**
|
||||
* Main program
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
/* create connection to the X server */
|
||||
Display *dpy = XOpenDisplay(":0");
|
||||
if (!dpy) {
|
||||
printf("Error: Cannot open display\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* init event tracker library */
|
||||
if (!xev_track_init(dpy))
|
||||
return -6;
|
||||
|
||||
/* busy loop polls X events */
|
||||
for (;;) {
|
||||
XEvent ev;
|
||||
XNextEvent(dpy, &ev);
|
||||
|
||||
/*
|
||||
* By calling this function, the callbacks defined above are
|
||||
* triggered.
|
||||
*/
|
||||
xev_track_handle_event(dpy, ev);
|
||||
xev_track_handle_cursor(dpy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
TARGET = test-xev_track
|
||||
REQUIRES = host x11 xtest xdamage
|
||||
SRC_CC = main.cc
|
||||
LIBS = xev_track
|
||||
|
||||
EXT_OBJECTS += -lX11 -lXdamage /usr/lib/libXtst.so.6
|
Loading…
x
Reference in New Issue
Block a user