diff options
author | Julien Cristau <jcristau@debian.org> | 2008-12-01 21:33:35 +0100 |
---|---|---|
committer | Julien Cristau <jcristau@debian.org> | 2008-12-01 21:33:35 +0100 |
commit | 63ba316bcbe8ad61ba63d9fe62c82e7d56dcc399 (patch) | |
tree | 5bc8d1de1474f1cf502611948c8ff15313d2b921 | |
parent | a813c4da7f0b166ee9001fa97c5d8d64e5b5b560 (diff) | |
parent | 1b95e32b4b0a4a114e0fbebe8a18316d2f9010cf (diff) |
Merge branch 'transform-proposal' of git.freedesktop.org:/git/xorg/app/xrandr
-rw-r--r-- | Makefile.am | 9 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | keystone.5c | 555 | ||||
-rw-r--r-- | xrandr.c | 338 |
4 files changed, 882 insertions, 21 deletions
diff --git a/Makefile.am b/Makefile.am index 5772a02..e7e460d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,15 @@ bin_PROGRAMS = xrandr +bin_SCRIPTS = xkeystone + +xkeystone: keystone.5c + (echo '#!/usr/bin/env nickle'; cat keystone.5c) > $@ + chmod +x $@ + +clean-local: + rm -f xkeystone + AM_CFLAGS = $(XRANDR_CFLAGS) xrandr_LDADD = $(XRANDR_LIBS) diff --git a/configure.ac b/configure.ac index 126a51b..5e563c8 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,7 @@ AM_CONFIG_HEADER(config.h) AC_PROG_CC AC_PROG_INSTALL +AC_CHECK_LIB(m,floor) # Checks for pkg-config packages PKG_CHECK_MODULES(XRANDR, xrandr >= 1.2.0 xrender x11) AC_SUBST(XRANDR_CFLAGS) diff --git a/keystone.5c b/keystone.5c new file mode 100644 index 0000000..228c020 --- /dev/null +++ b/keystone.5c @@ -0,0 +1,555 @@ +/* + * Copyright © 2008 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +autoload Process; +autoload Nichrome; +autoload Nichrome::Box; +autoload Nichrome::Label; +autoload Nichrome::Button; + +extend namespace Nichrome { + public namespace Quad { + public typedef quad_t; + public typedef widget_t + struct { + point_t[4] p; + real line_width; + real corner_diameter; + rgba_color_t line_color; + rgba_color_t corner_color; + bool down; + bool started; + int active; + void(&quad_t) callback; + } quad_t; + + protected void outline (cairo_t cr, &quad_t quad) { + for (int i = 0; i < dim (quad.p); i++) { + arc (cr, quad.p[i].x, quad.p[i].y, + quad.corner_diameter / 2, 0, 2 * pi); + close_path (cr); + } + } + + void text_at (cairo_t cr, point_t p, string text) { + text_extents_t e = text_extents (cr, text); + p.x = p.x - e.width / 2 - e.x_bearing; + p.y = p.y - e.height / 2 - e.y_bearing; + move_to (cr, p.x, p.y); + show_text (cr, text); + } + + protected void draw (cairo_t cr, &quad_t quad) { + if (!quad.started) { + quad.p[2].x = quad.p[1].x = quad.geometry.width; + quad.p[3].y = quad.p[2].y = quad.geometry.height; + quad.started = true; + } + rectangle (cr, 0, 0, quad.geometry.width, quad.geometry.height); + set_source_rgba (cr, 0, 0, 0, .25); + fill (cr); + for (int i = 0; i < dim (quad.p); i++) + line_to (cr, quad.p[i].x, quad.p[i].y); + close_path (cr); + set_line_width (cr, quad.line_width); + set_source_rgba (cr, quad.line_color.red, quad.line_color.green, + quad.line_color.blue, quad.line_color.alpha); + set_line_join (cr, line_join_t.ROUND); + stroke (cr); + set_source_rgba (cr, quad.corner_color.red, quad.corner_color.green, + quad.corner_color.blue, quad.corner_color.alpha); + outline (cr, &quad); + fill (cr); + set_source_rgba (cr, 1, 1, 1, 1); + for (int i = 0; i < dim (quad.p); i++) + text_at (cr, quad.p[i], sprintf ("%d", i)); + } + + int nearest (&quad_t quad, point_t p) { + real best_dist2 = 0; + int best = 0; + + for (int i = 0; i < dim (quad.p); i++) { + real dist2 = ((p.x - quad.p[i].x) ** 2 + + (p.y - quad.p[i].y) ** 2); + if (i == 0 || dist2 < best_dist2) { + best_dist2 = dist2; + best = i; + } + } + return best; + } + + protected void button (&quad_t quad, &button_event_t event) { + enum switch (event.type) { + case press: + quad.down = true; + quad.active = nearest (&quad, event); + break; + case release: + quad.down = false; + break; + default: + break; + } + } + + protected void motion (&quad_t quad, &motion_event_t motion) { + if (quad.down) { + motion.x = max (0, min (quad.geometry.width, motion.x)); + motion.y = max (0, min (quad.geometry.height, motion.y)); + quad.p[quad.active].x = motion.x; + quad.p[quad.active].y = motion.y; + quad.callback (&quad); + Widget::reoutline (&quad); + Widget::redraw (&quad); + } + } + + protected void configure (&quad_t quad, + rect_t geometry) + { + if (quad.geometry.width > 0 && quad.geometry.height > 0) + { + real x_scale = geometry.width / quad.geometry.width; + real y_scale = geometry.height / quad.geometry.height; + for (int i = 0; i< 4; i++) { + quad.p[i].x *= x_scale; + quad.p[i].y *= y_scale; + } + } + Widget::configure (&quad, geometry); + quad.callback (&quad); + } + + protected void init (&quad_t quad, + &nichrome_t nichrome, + void (&quad_t) callback) { + Widget::init (&nichrome, &quad); + quad.outline = outline; + quad.draw = draw; + quad.button = button; + quad.motion = motion; + quad.configure = configure; + quad.p = (point_t[4]) { + { x = 0, y = 0 } ... + }; + quad.line_color = (rgba_color_t) { + red = 1, green = 0, blue = 0, alpha = .5 + }; + quad.line_width = 10; + quad.corner_color = (rgba_color_t) { + red = 0, green = 0, blue = 1, alpha = 0.75 + }; + quad.corner_diameter = 20; + quad.down = false; + quad.active = -1; + quad.callback = callback; + quad.started = false; + } + + protected *quad_t new (&nichrome_t nichrome, void(&quad_t) callback) { + quad_t quad; + + init (&quad, &nichrome, callback); + return &quad; + } + } +} +import Nichrome; +import Nichrome::Box; +import Nichrome::Label; +import Nichrome::Button; +import Nichrome::Quad; + +import Cairo; +typedef real[3,3] m_t; +typedef point_t[4] q_t; + +/* + * Ok, given an source quad and a dest rectangle, compute + * a transform that maps the rectangle to q. That's easier + * as the rectangle has some nice simple properties. Invert + * the matrix to find the opposite mapping + * + * q0 q1 + * + * q3 q2 + * + * | m00 m01 m02 | + * | m10 m11 m12 | + * | m20 m21 m22 | + * + * m [ 0 0 1 ] = q[0] + * + * Set m22 to 1, and solve: + * + * | m02 , m12 , 1 | = | q0x, q0y, 1 | + * + * | m00 * w + q0x m10 * w + q0y | + * | ------------- , ------------- , 1 | = | q1x, q1y, 1 | + * | m20 * w + 1 m20 * w + 1 | + + * m00*w + q0x = q1x*(m20*w + 1) + * m00 = m20*q1x + (q1x - q0x) / w; + * + * m10*w + q0y = q1y*(m20*w + 1) + * m10 = m20*q1y + (q1y - q0y) / w; + * + * m01*h + q0x = q3x*(m21*h + 1) + * m01 = m21*q3x + (q3x - q0x) / h; + * + * m11*h + q0y = q3y*(m21*h + 1) + * m11 = m21*q3y + (q3y - q0y) / h + * + * m00*w + m01*h + q0x = q2x*(m20*w + m21*h + 1) + * + * m20*q1x*w + q1x - q0x + m21*q3x*h + q3x - q0x + q0x = m20*q2x*w + m21*q2x*h + q2x + * + * m20*q1x*w - m20*q2x*w = m21*q2x*h - m21*q3x*h + q2x - q1x + q0x - q3x + q0x - q0x + * + * m20*(q1x - q2x)*w = m21*(q2x - q3x)*h + q2x - q1x - q3x + q0x + * + * + * m10*w + m11*h + q0y = q2y*(m20*w + m21*h + 1) + * + * m20*q1y*w + q1y - q0y + m21*q3y*h + q3y - q0y + q0y = m20*q2y*w + m21*q2y*h + q2y + * + * m20*q1y*w - m20*q2y*w = m21*q2y*h - m21*q3y*h + q2y - q1y + q0y - q3y + q0y - q0y + * + * m20*(q1y - q2y)*w = m21*(q2y - q3y)*h + q2y - q1y - q3y + q0y + * + * + * m20*(q1x - q2x)*(q1y - q2y)*w = m21*(q2x - q3x)*(q1y - q2y)*h + (q2x - q1x - q3x + q0x)*(q1y - q2y) + * + * m20*(q1y - q2y)*(q1x - q2x)*w = m21*(q2y - q3y)*(q1x - q2x)*h + (q2y - q1y - q3y + q0y)*(q1x - q2x) + * + * 0 = m21*((q2x - q3x)*(q1y - q2y) - (q2y - q3y)*(q1x - q2x))*h + (stuff) + * = m21 * a + b; + * + * m21 = -(stuff) / (other stuff) + * + * m20 = f(m21) + * + * m00 = f(m20) + * m10 = f(m20) + * + * m01 = f(m21) + * m11 = f(m21) + * + * done. + */ +m_t solve (q_t q, real w, real h) +{ + real q0x = q[0].x, q0y = q[0].y; + real q1x = q[1].x, q1y = q[1].y; + real q2x = q[2].x, q2y = q[2].y; + real q3x = q[3].x, q3y = q[3].y; + real m00, m01, m02; + real m10, m11, m12; + real m20, m21, m22; + + m02 = q0x; + m12 = q0y; + m22 = 1; + + real a = ((q2x - q3x)*(q1y - q2y) - (q2y - q3y)*(q1x - q2x)) * h; + real b = (q2x - q1x - q3x + q0x) * (q1y - q2y) - (q2y - q1y - q3y + q0y) * (q1x - q2x); + m21 = - b / a; + + if (q1x != q2x) + m20 = (m21 * (q2x - q3x) * h + q2x - q1x - q3x + q0x) / ((q1x - q2x) * w); + else + m20 = (m21 * (q2y - q3y) * h + q2y - q1y - q3y + q0y) / ((q1y - q2y) * w); + + m00 = m20 * q1x + (q1x - q0x) / w; + m10 = m20 * q1y + (q1y - q0y) / w; + + m01 = m21 * q3x + (q3x - q0x) / h; + m11 = m21 * q3y + (q3y - q0y) / h; + + return (m_t) { + { m00, m01, m02 }, + { m10, m11, m12 }, + { m20, m21, m22 } }; +} + +m_t +invert (m_t m) +{ + real det; + int i, j; + m_t r; + + static int[3] a = { 2, 2, 1 }; + static int[3] b = { 1, 0, 0 }; + + det = 0; + for (i = 0; i < 3; i++) { + real p; + int ai = a[i]; + int bi = b[i]; + p = m[i,0] * (m[ai,2] * m[bi,1] - m[ai,1] * m[bi,2]); + if (i == 1) + p = -p; + det += p; + } + det = 1/det; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + real p; + int ai = a[i]; + int aj = a[j]; + int bi = b[i]; + int bj = b[j]; + + p = m[ai,aj] * m[bi,bj] - m[ai,bj] * m[bi,aj]; + if (((i + j) & 1) != 0) + p = -p; + r[j,i] = det * p; + } + } + return r; +} + +m_t +rescale (m_t m, real limit) +{ + real max = 0; + for (int j = 0; j < 3; j++) + for (int i = 0; i < 3; i++) + if ((real v = abs (m[j,i])) > max) + max = v; + real scale = limit / max; + for (int j = 0; j < 3; j++) + for (int i = 0; i < 3; i++) + m[j,i] *= scale; + return m; + +} + +string +m_print (m_t m) +{ + /* + return sprintf ("%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f", + m[0,0],m[0,1],m[0,2], + m[1,0],m[1,1],m[1,2], + m[2,0],m[2,1],m[2,2]); + */ + return sprintf ("%v,%v,%v,%v,%v,%v,%v,%v,%v", + m[0,0],m[0,1],m[0,2], + m[1,0],m[1,1],m[1,2], + m[2,0],m[2,1],m[2,2]); +} + +int +fixed (real x) +{ + return floor (x * 65536 + 0.5) & 0xffffffff; +} + +void +m_print_fix (m_t m) +{ + for (int i = 0; i < 3; i++) + { + printf (" { 0x%08x, 0x%08x, 0x%08x },\n", + fixed (m[i,0]), fixed (m[i,1]), fixed (m[i,2])); + } +} + +string +m_row (m_t m, int row) +{ + return sprintf ("%10.5f %10.5f %10.5f", + m[row,0],m[row,1],m[row,2]); +} + +Cairo::point_t[*] scale(Cairo::point_t[*] p, real w, real h) +{ + for (int i = 0; i < dim (p); i++) { + p[i].x *= w; + p[i].y *= h; + } + return p; +} + +typedef struct { + string name; + rect_t geometry; +} output_t; + +autoload Process; + + +output_t[*] get_outputs () { + output_t[...] outputs = {}; + twixt (file randr = Process::popen (Process::popen_direction.read, + false, "xrandr", "xrandr"); + File::close (randr)) + { + while (!File::end (randr)) { + string[*] words = String::wordsplit (File::fgets (randr), " "); + if (dim (words) >= 3 && words[1] == "connected" && + File::sscanf (words[2], "%dx%d+%d+%d", + &(int width), &(int height), + &(int x), &(int y)) == 4) + { + outputs[dim(outputs)] = (output_t) { + name = words[0], + geometry = { + x = x, y = y, width = width, height = height + } + }; + } + } + } + return outputs; +} + +void main () +{ + m_t m = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }, m_i, m_r; + bool m_available = true; + output_t[*] outputs = get_outputs (); + output_t target_output; + + if (dim (outputs) == 0) { + File::fprintf (stderr, "%s: No enabled outputs\n", argv[0]); + exit (1); + } + + if (dim (argv) > 1) { + int i; + for (i = 0; i < dim (outputs); i++) + if (argv[1] == outputs[i].name) { + target_output = outputs[i]; + break; + } + if (i == dim (outputs)) { + File::fprintf (stderr, "%s: no enabled output \"%s\"\n", + argv[0], argv[1]); + exit (1); + } + } + else + target_output = outputs[0]; + + real target_width = target_output.geometry.width; + real target_height = target_output.geometry.height; + + real screen_width = 0; + real screen_height = 0; + + for (int i = 0; i < dim (outputs); i++) + { + screen_width = max (screen_width, + outputs[i].geometry.x + + outputs[i].geometry.width); + screen_height = max (screen_height, + outputs[i].geometry.y + + outputs[i].geometry.height); + } + + &nichrome_t nichrome = Nichrome::new ("Keystone Correction", 400, 350); + + (*label_t)[3] label; + &label_t space = Label::new (&nichrome, ""); + for (int i = 0; i < 3; i++) { + label[i] = Label::new (&nichrome, "matrix"); + label[i]->font = "sans-9"; + } + + void callback (&quad_t quad) { + real w = quad.geometry.width; + real h = quad.geometry.height; + string[3] text; + try { + m = solve (scale (quad.p, target_width / w, target_height / h), + target_width, target_height); + m_i = invert (m); + m_r = rescale (m_i, 16384); + for (int i = 0; i < 3; i++) + text[i] = m_row (m_i,i); + m_available = true; + } catch divide_by_zero (real a, real b) { + text = (string[3]) { "no solution", "" ... }; + m_available = false; + } + for (int i = 0; i < 3; i++) + Label::relabel (label[i], text[i]); + } + &quad_t quad = Quad::new (&nichrome, callback); + + void doit_func (&widget_t widget, bool state) + { + if (m_available) + { + Process::system ("xrandr", + "xrandr", + "--fb", + sprintf ("%dx%d", screen_width, screen_height), + "--output", + target_output.name, + "--transform", + m_print (m_r)); + } + } + + &button_t doit = Button::new (&nichrome, "doit", doit_func); + + void show_func (&widget_t widget, bool state) + { + if (m_available) + { + printf ("normal: %s\n", m_print (m)); + printf ("inverse: %s\n", m_print (m_i)); + printf ("scaled: %s\n", m_print (m_r)); + printf ("fixed:\n"); + m_print_fix (m_i); + } + } + + &button_t show = Button::new (&nichrome, "show", show_func); + &button_t quit = Button::new (&nichrome, "quit", + void func (&widget_t w, bool state) { + w.nichrome.running = false; + }); + + &box_t hbox = Box::new (Box::dir_t.horizontal, + Box::widget_item (&doit, 0), + Box::widget_item (&show, 0), + + Box::widget_item (&quit, 0), + Box::glue_item (1)); + &box_t box = Box::new (Box::dir_t.vertical, + Box::box_item (&hbox), + Box::widget_item (label[0], 0), + Box::widget_item (label[1], 0), + Box::widget_item (label[2], 0), + Box::widget_item (&space, 0), + Box::widget_item (&quad, 1)); + Nichrome::set_box (&nichrome, &box); + Nichrome::main_loop (&nichrome); +} + +main (); @@ -129,6 +129,8 @@ usage(void) fprintf(stderr, " --below <output>\n"); fprintf(stderr, " --same-as <output>\n"); fprintf(stderr, " --set <property> <value>\n"); + fprintf(stderr, " --scale <x>x<y>\n"); + fprintf(stderr, " --transform <a>,<b>,<c>,<d>,<e>,<f>,<g>,<h>,<i>\n"); fprintf(stderr, " --off\n"); fprintf(stderr, " --crtc <crtc>\n"); fprintf(stderr, " --newmode <name> <clock MHz>\n"); @@ -157,6 +159,17 @@ fatal (const char *format, ...) /*NOTREACHED*/ } +static void +warning (const char *format, ...) +{ + va_list ap; + + va_start (ap, format); + fprintf (stderr, "%s: ", program_name); + vfprintf (stderr, format, ap); + va_end (ap); +} + static char * rotation_name (Rotation rotation) { @@ -196,6 +209,18 @@ typedef enum _relation { left_of, right_of, above, below, same_as, } relation_t; +typedef struct { + int x, y, width, height; +} rectangle_t; + +typedef struct { + int x1, y1, x2, y2; +} box_t; + +typedef struct { + int x, y; +} point_t; + typedef enum _changes { changes_none = 0, changes_crtc = (1 << 0), @@ -207,6 +232,7 @@ typedef enum _changes { changes_automatic = (1 << 6), changes_refresh = (1 << 7), changes_property = (1 << 8), + changes_transform = (1 << 9), } changes_t; typedef enum _name_kind { @@ -226,9 +252,17 @@ typedef struct { typedef struct _crtc crtc_t; typedef struct _output output_t; +typedef struct _transform transform_t; typedef struct _umode umode_t; typedef struct _output_prop output_prop_t; +struct _transform { + XTransform transform; + char *filter; + int nparams; + XFixed *params; +}; + struct _crtc { name_t crtc; Bool changing; @@ -240,6 +274,7 @@ struct _crtc { Rotation rotation; output_t **outputs; int noutput; + transform_t current_transform, pending_transform; }; struct _output_prop { @@ -275,6 +310,7 @@ struct _output { Rotation rotation; Bool automatic; + transform_t transform; }; typedef enum _umode_action { @@ -353,6 +389,81 @@ mode_width (XRRModeInfo *mode_info, Rotation rotation) } } +Bool +transform_point (XTransform *transform, double *xp, double *yp) +{ + double vector[3]; + double result[3]; + int i, j; + double partial, v; + + vector[0] = *xp; + vector[1] = *yp; + vector[2] = 1; + for (j = 0; j < 3; j++) + { + v = 0; + for (i = 0; i < 3; i++) + v += (XFixedToDouble (transform->matrix[j][i]) * vector[i]); + if (v > 32767 || v < -32767) + return False; + result[j] = v; + } + if (!result[2]) + return False; + for (j = 0; j < 2; j++) + vector[j] = result[j] / result[2]; + *xp = vector[0]; + *yp = vector[1]; + return True; +} + +Bool +path_bounds (XTransform *transform, point_t *points, int npoints, box_t *box) +{ + int i; + box_t point; + + for (i = 0; i < npoints; i++) { + double x, y; + x = points[i].x; + y = points[i].y; + transform_point (transform, &x, &y); + point.x1 = floor (x); + point.y1 = floor (y); + point.x2 = ceil (x); + point.y2 = ceil (y); + if (i == 0) + *box = point; + else { + if (point.x1 < box->x1) box->x1 = point.x1; + if (point.y1 < box->y1) box->y1 = point.y1; + if (point.x2 > box->x2) box->x2 = point.x2; + if (point.y2 > box->y2) box->y2 = point.y2; + } + } +} + +static void +mode_geometry (XRRModeInfo *mode_info, Rotation rotation, + XTransform *transform, + box_t *bounds) +{ + point_t rect[4]; + int width = mode_width (mode_info, rotation); + int height = mode_height (mode_info, rotation); + + rect[0].x = 0; + rect[0].y = 0; + rect[1].x = width; + rect[1].y = 0; + rect[2].x = width; + rect[2].y = height; + rect[3].x = 0; + rect[3].y = height; + path_bounds (transform, rect, 4, bounds); +} + /* v refresh frequency in Hz */ static float mode_refresh (XRRModeInfo *mode_info) @@ -442,6 +553,53 @@ set_name (name_t *name, char *string, name_kind_t valid) usage (); } +static void +init_transform (transform_t *transform) +{ + int x; + memset (&transform->transform, '\0', sizeof (transform->transform)); + for (x = 0; x < 3; x++) + transform->transform.matrix[x][x] = XDoubleToFixed (1.0); + transform->filter = ""; + transform->nparams = 0; + transform->params = NULL; +} + +static void +set_transform (transform_t *dest, + XTransform *transform, + char *filter, + XFixed *params, + int nparams) +{ + dest->transform = *transform; + dest->filter = strdup (filter); + dest->nparams = nparams; + dest->params = malloc (nparams * sizeof (XFixed)); + memcpy (dest->params, params, nparams * sizeof (XFixed)); +} + +static void +copy_transform (transform_t *dest, transform_t *src) +{ + set_transform (dest, &src->transform, + src->filter, src->params, src->nparams); +} + +static Bool +equal_transform (transform_t *a, transform_t *b) +{ + if (memcmp (&a->transform, &b->transform, sizeof (XTransform)) != 0) + return False; + if (strcmp (a->filter, b->filter) != 0) + return False; + if (a->nparams != b->nparams) + return False; + if (memcmp (a->params, b->params, a->nparams * sizeof (XFixed)) != 0) + return False; + return True; +} + static output_t * add_output (void) { @@ -687,6 +845,17 @@ crtc_can_use_rotation (crtc_t *crtc, Rotation rotation) return False; } +static Bool +crtc_can_use_transform (crtc_t *crtc, XTransform *transform) +{ + int major, minor; + + XRRQueryVersion (dpy, &major, &minor); + if (major > 1 || (major == 1 && minor >= 3)) + return True; + return False; +} + /* * Report only rotations that are supported by all crtcs */ @@ -738,8 +907,8 @@ set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info) { /* sanity check output info */ if (output_info->connection != RR_Disconnected && !output_info->nmode) - fatal ("Output %s is not disconnected but has no modes\n", - output_info->name); + warning ("Output %s is not disconnected but has no modes\n", + output_info->name); /* set output name and info */ if (!(output->output.kind & name_xid)) @@ -844,6 +1013,15 @@ set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info) output->output.string, rotation_name (output->rotation), reflection_name (output->rotation)); + + /* set transformation */ + if (!(output->changes & changes_transform)) + { + if (output->crtc_info) + copy_transform (&output->transform, &output->crtc_info->current_transform); + else + init_transform (&output->transform); + } } static void @@ -871,6 +1049,10 @@ get_crtcs (void) for (c = 0; c < res->ncrtc; c++) { XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (dpy, res, res->crtcs[c]); +#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3 + XRRCrtcTransformAttributes *attr; +#endif + int x; set_name_xid (&crtcs[c].crtc, res->crtcs[c]); set_name_index (&crtcs[c].crtc, c); if (!crtc_info) fatal ("could not get crtc 0x%x information", res->crtcs[c]); @@ -882,7 +1064,22 @@ get_crtcs (void) crtcs[c].y = 0; crtcs[c].rotation = RR_Rotate_0; } - } +#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3 + if (XRRGetCrtcTransform (dpy, res->crtcs[c], &attr) && attr) { + set_transform (&crtcs[c].current_transform, + &attr->currentTransform, + attr->currentFilter, + attr->currentParams, + attr->currentNparams); + XFree (attr); + } + else +#endif + { + init_transform (&crtcs[c].current_transform); + } + copy_transform (&crtcs[c].pending_transform, &crtcs[c].current_transform); + } } static void @@ -897,7 +1094,8 @@ crtc_add_output (crtc_t *crtc, output_t *output) crtc->y = output->y; crtc->rotation = output->rotation; crtc->mode_info = output->mode_info; - } + copy_transform (&crtc->pending_transform, &output->transform); + } if (!crtc->outputs) fatal ("out of memory"); crtc->outputs[crtc->noutput++] = output; } @@ -926,6 +1124,20 @@ crtc_disable (crtc_t *crtc) 0, 0, None, RR_Rotate_0, NULL, 0); } +static void +crtc_set_transform (crtc_t *crtc, transform_t *transform) +{ + int major, minor; + + XRRQueryVersion (dpy, &major, &minor); + if (major > 1 || (major == 1 && minor >= 3)) + XRRSetCrtcTransform (dpy, crtc->crtc.xid, + &transform->transform, + transform->filter, + transform->params, + transform->nparams); +} + static Status crtc_revert (crtc_t *crtc) { @@ -936,6 +1148,8 @@ crtc_revert (crtc_t *crtc) if (dryrun) return RRSetConfigSuccess; + + crtc_set_transform (crtc, &crtc->current_transform); return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime, crtc_info->x, crtc_info->y, crtc_info->mode, crtc_info->rotation, @@ -971,9 +1185,12 @@ crtc_apply (crtc_t *crtc) if (dryrun) s = RRSetConfigSuccess; else + { + crtc_set_transform (crtc, &crtc->pending_transform); s = XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime, crtc->x, crtc->y, mode, crtc->rotation, rr_outputs, crtc->noutput); + } free (rr_outputs); return s; } @@ -1079,16 +1296,21 @@ apply (void) { XRRModeInfo *old_mode = find_mode_by_xid (crtc_info->mode); int x, y, w, h; + box_t bounds; if (!old_mode) panic (RRSetConfigFailed, crtc); /* old position and size information */ - x = crtc_info->x; - y = crtc_info->y; - w = mode_width (old_mode, crtc_info->rotation); - h = mode_height (old_mode, crtc_info->rotation); - + mode_geometry (old_mode, crtc_info->rotation, + &crtc->current_transform.transform, + &bounds); + + x = crtc_info->x + bounds.x1; + y = crtc_info->y + bounds.y1; + w = bounds.x2 - bounds.x1; + h = bounds.y2 - bounds.y1; + /* if it fits, skip it */ if (x + w <= fb_width && y + h <= fb_height) continue; @@ -1287,6 +1509,8 @@ check_crtc_for_output (crtc_t *crtc, output_t *output, Bool ignore_state) return False; if (crtc->rotation != output->rotation) return False; + if (!equal_transform (&crtc->current_transform, &output->transform)) + return False; } else if (crtc->crtc_info->noutput) { @@ -1433,19 +1657,23 @@ set_screen_size (void) { XRRModeInfo *mode_info = output->mode_info; int x, y, w, h; + box_t bounds; if (!mode_info) continue; - x = output->x; - y = output->y; - w = mode_width (mode_info, output->rotation); - h = mode_height (mode_info, output->rotation); + mode_geometry (mode_info, output->rotation, + &output->transform.transform, + &bounds); + x = output->x + bounds.x1; + y = output->y + bounds.y1; + w = bounds.x2 - bounds.x1; + h = bounds.y2 - bounds.y1; /* make sure output fits in specified size */ if (fb_specified) { if (x + w > fb_width || y + h > fb_height) - fatal ("specified screen %dx%d not large enough for output %s (%dx%d+%d+%d)\n", - fb_width, fb_height, output->output.string, w, h, x, y); + warning ("specified screen %dx%d not large enough for output %s (%dx%d+%d+%d)\n", + fb_width, fb_height, output->output.string, w, h, x, y); } /* fit fb to output */ else @@ -1856,6 +2084,50 @@ main (int argc, char **argv) setit_1_2 = True; continue; } + if (!strcmp ("--scale", argv[i])) + { + double sx, sy; + if (++i>=argc) usage(); + if (sscanf (argv[i], "%lfx%lf", &sx, &sy) != 2) + usage (); + init_transform (&output->transform); + output->transform.transform.matrix[0][0] = XDoubleToFixed (sx); + output->transform.transform.matrix[1][1] = XDoubleToFixed (sy); + output->transform.transform.matrix[2][2] = XDoubleToFixed (1.0); + if (sx != 1 || sy != 1) + output->transform.filter = "bilinear"; + else + output->transform.filter = "nearest"; + output->transform.nparams = 0; + output->transform.params = NULL; + output->changes |= changes_transform; + continue; + } + if (!strcmp ("--transform", argv[i])) { + double transform[3][3]; + int k, l; + if (++i>=argc) usage (); + init_transform (&output->transform); + if (strcmp (argv[i], "none") != 0) + { + if (sscanf(argv[i], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", + &transform[0][0],&transform[0][1],&transform[0][2], + &transform[1][0],&transform[1][1],&transform[1][2], + &transform[2][0],&transform[2][1],&transform[2][2]) + != 9) + usage (); + init_transform (&output->transform); + for (k = 0; k < 3; k++) + for (l = 0; l < 3; l++) { + output->transform.transform.matrix[k][l] = XDoubleToFixed (transform[k][l]); + } + output->transform.filter = "bilinear"; + output->transform.nparams = 0; + output->transform.params = NULL; + } + output->changes |= changes_transform; + continue; + } if (!strcmp ("--off", argv[i])) { if (!output) usage(); set_name_xid (&output->mode, None); @@ -2261,6 +2533,8 @@ main (int argc, char **argv) for (output = outputs; output; output = output->next) { XRROutputInfo *output_info = output->output_info; + crtc_t *crtc = output->crtc_info; + XRRCrtcInfo *crtc_info = crtc ? crtc->crtc_info : NULL; XRRModeInfo *mode = output->mode_info; Atom *props; int j, k, nprop; @@ -2270,10 +2544,14 @@ main (int argc, char **argv) printf ("%s %s", output_info->name, connection[output_info->connection]); if (mode) { - printf (" %dx%d+%d+%d", - mode_width (mode, output->rotation), - mode_height (mode, output->rotation), - output->x, output->y); + if (crtc_info) { + printf (" %dx%d+%d+%d", + crtc_info->width, crtc_info->height, + crtc_info->x, crtc_info->y); + } else { + printf (" %dx%d+%d+%d", + mode->width, mode->height, output->x, output->y); + } if (verbose) printf (" (0x%x)", mode->id); if (output->rotation != RR_Rotate_0 || verbose) @@ -2339,6 +2617,22 @@ main (int argc, char **argv) } printf ("\n"); } + if (verbose) + { + int x, y; + + printf ("\tTransform: "); + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + printf (" %f", XFixedToDouble (output->transform.transform.matrix[y][x])); + if (y < 2) + printf ("\n\t "); + } + if (output->transform.filter) + printf ("\n\t filter: %s", output->transform.filter); + printf ("\n"); + } if (verbose || properties) { props = XRRListOutputProperties (dpy, output->output.xid, @@ -2406,12 +2700,14 @@ main (int argc, char **argv) } } printf("\n"); - } else if (actual_format == 8) { printf ("\t\t%s: %s%s\n", XGetAtomName (dpy, props[j]), prop, bytes_after ? "..." : ""); } else { - printf ("\t\t%s: ????\n", XGetAtomName (dpy, props[j])); + char *type = actual_type ? XGetAtomName (dpy, actual_type) : "none"; + printf ("\t\t%s: %s(%d) (format %d items %d) ????\n", + XGetAtomName (dpy, props[j]), + type, actual_type, actual_format, nitems); } free(propinfo); |