keystone.5c revision 966b534a
162df5ad0Smrg/* 262df5ad0Smrg * Copyright © 2008 Keith Packard 362df5ad0Smrg * 462df5ad0Smrg * Permission to use, copy, modify, distribute, and sell this software and its 562df5ad0Smrg * documentation for any purpose is hereby granted without fee, provided that 662df5ad0Smrg * the above copyright notice appear in all copies and that both that copyright 762df5ad0Smrg * notice and this permission notice appear in supporting documentation, and 862df5ad0Smrg * that the name of the copyright holders not be used in advertising or 962df5ad0Smrg * publicity pertaining to distribution of the software without specific, 1062df5ad0Smrg * written prior permission. The copyright holders make no representations 1162df5ad0Smrg * about the suitability of this software for any purpose. It is provided "as 1262df5ad0Smrg * is" without express or implied warranty. 1362df5ad0Smrg * 1462df5ad0Smrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1562df5ad0Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 1662df5ad0Smrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 1762df5ad0Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 1862df5ad0Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 1962df5ad0Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 2062df5ad0Smrg * OF THIS SOFTWARE. 2162df5ad0Smrg */ 2262df5ad0Smrg 2362df5ad0Smrgautoload Process; 2462df5ad0Smrgautoload Nichrome; 2562df5ad0Smrgautoload Nichrome::Box; 2662df5ad0Smrgautoload Nichrome::Label; 2762df5ad0Smrgautoload Nichrome::Button; 2862df5ad0Smrg 2962df5ad0Smrgextend namespace Nichrome { 3062df5ad0Smrg public namespace Quad { 3162df5ad0Smrg public typedef quad_t; 3262df5ad0Smrg public typedef widget_t + struct { 3362df5ad0Smrg point_t[4] p; 3462df5ad0Smrg real line_width; 3562df5ad0Smrg real corner_diameter; 3662df5ad0Smrg rgba_color_t line_color; 3762df5ad0Smrg rgba_color_t corner_color; 3862df5ad0Smrg bool down; 3962df5ad0Smrg bool started; 40966b534aSmrg int active_corner; 4162df5ad0Smrg void(&quad_t) callback; 4262df5ad0Smrg } quad_t; 4362df5ad0Smrg 4462df5ad0Smrg protected void outline (cairo_t cr, &quad_t quad) { 4562df5ad0Smrg for (int i = 0; i < dim (quad.p); i++) { 4662df5ad0Smrg arc (cr, quad.p[i].x, quad.p[i].y, 4762df5ad0Smrg quad.corner_diameter / 2, 0, 2 * pi); 4862df5ad0Smrg close_path (cr); 4962df5ad0Smrg } 5062df5ad0Smrg } 5162df5ad0Smrg 52966b534aSmrg protected void natural (cairo_t cr, &quad_t quad) { 53966b534aSmrg rectangle (cr, 0, 0, 256, 256); 54966b534aSmrg } 55966b534aSmrg 5662df5ad0Smrg void text_at (cairo_t cr, point_t p, string text) { 5762df5ad0Smrg text_extents_t e = text_extents (cr, text); 5862df5ad0Smrg p.x = p.x - e.width / 2 - e.x_bearing; 5962df5ad0Smrg p.y = p.y - e.height / 2 - e.y_bearing; 6062df5ad0Smrg move_to (cr, p.x, p.y); 6162df5ad0Smrg show_text (cr, text); 6262df5ad0Smrg } 6362df5ad0Smrg 6462df5ad0Smrg protected void draw (cairo_t cr, &quad_t quad) { 6562df5ad0Smrg if (!quad.started) { 6662df5ad0Smrg quad.p[2].x = quad.p[1].x = quad.geometry.width; 6762df5ad0Smrg quad.p[3].y = quad.p[2].y = quad.geometry.height; 6862df5ad0Smrg quad.started = true; 6962df5ad0Smrg } 7062df5ad0Smrg rectangle (cr, 0, 0, quad.geometry.width, quad.geometry.height); 7162df5ad0Smrg set_source_rgba (cr, 0, 0, 0, .25); 7262df5ad0Smrg fill (cr); 7362df5ad0Smrg for (int i = 0; i < dim (quad.p); i++) 7462df5ad0Smrg line_to (cr, quad.p[i].x, quad.p[i].y); 7562df5ad0Smrg close_path (cr); 7662df5ad0Smrg set_line_width (cr, quad.line_width); 7762df5ad0Smrg set_source_rgba (cr, quad.line_color.red, quad.line_color.green, 7862df5ad0Smrg quad.line_color.blue, quad.line_color.alpha); 7962df5ad0Smrg set_line_join (cr, line_join_t.ROUND); 8062df5ad0Smrg stroke (cr); 8162df5ad0Smrg set_source_rgba (cr, quad.corner_color.red, quad.corner_color.green, 8262df5ad0Smrg quad.corner_color.blue, quad.corner_color.alpha); 8362df5ad0Smrg outline (cr, &quad); 8462df5ad0Smrg fill (cr); 8562df5ad0Smrg set_source_rgba (cr, 1, 1, 1, 1); 8662df5ad0Smrg for (int i = 0; i < dim (quad.p); i++) 8762df5ad0Smrg text_at (cr, quad.p[i], sprintf ("%d", i)); 8862df5ad0Smrg } 8962df5ad0Smrg 9062df5ad0Smrg int nearest (&quad_t quad, point_t p) { 9162df5ad0Smrg real best_dist2 = 0; 9262df5ad0Smrg int best = 0; 9362df5ad0Smrg 9462df5ad0Smrg for (int i = 0; i < dim (quad.p); i++) { 9562df5ad0Smrg real dist2 = ((p.x - quad.p[i].x) ** 2 + 9662df5ad0Smrg (p.y - quad.p[i].y) ** 2); 9762df5ad0Smrg if (i == 0 || dist2 < best_dist2) { 9862df5ad0Smrg best_dist2 = dist2; 9962df5ad0Smrg best = i; 10062df5ad0Smrg } 10162df5ad0Smrg } 10262df5ad0Smrg return best; 10362df5ad0Smrg } 10462df5ad0Smrg 10562df5ad0Smrg protected void button (&quad_t quad, &button_event_t event) { 10662df5ad0Smrg enum switch (event.type) { 10762df5ad0Smrg case press: 10862df5ad0Smrg quad.down = true; 109966b534aSmrg quad.active_corner = nearest (&quad, event); 11062df5ad0Smrg break; 11162df5ad0Smrg case release: 11262df5ad0Smrg quad.down = false; 11362df5ad0Smrg break; 11462df5ad0Smrg default: 11562df5ad0Smrg break; 11662df5ad0Smrg } 11762df5ad0Smrg } 11862df5ad0Smrg 11962df5ad0Smrg protected void motion (&quad_t quad, &motion_event_t motion) { 12062df5ad0Smrg if (quad.down) { 12162df5ad0Smrg motion.x = max (0, min (quad.geometry.width, motion.x)); 12262df5ad0Smrg motion.y = max (0, min (quad.geometry.height, motion.y)); 123966b534aSmrg quad.p[quad.active_corner].x = motion.x; 124966b534aSmrg quad.p[quad.active_corner].y = motion.y; 12562df5ad0Smrg quad.callback (&quad); 12662df5ad0Smrg Widget::reoutline (&quad); 12762df5ad0Smrg Widget::redraw (&quad); 12862df5ad0Smrg } 12962df5ad0Smrg } 13062df5ad0Smrg 13162df5ad0Smrg protected void configure (&quad_t quad, 13262df5ad0Smrg rect_t geometry) 13362df5ad0Smrg { 13462df5ad0Smrg if (quad.geometry.width > 0 && quad.geometry.height > 0) 13562df5ad0Smrg { 13662df5ad0Smrg real x_scale = geometry.width / quad.geometry.width; 13762df5ad0Smrg real y_scale = geometry.height / quad.geometry.height; 13862df5ad0Smrg for (int i = 0; i< 4; i++) { 13962df5ad0Smrg quad.p[i].x *= x_scale; 14062df5ad0Smrg quad.p[i].y *= y_scale; 14162df5ad0Smrg } 14262df5ad0Smrg } 14362df5ad0Smrg Widget::configure (&quad, geometry); 14462df5ad0Smrg quad.callback (&quad); 14562df5ad0Smrg } 14662df5ad0Smrg 14762df5ad0Smrg protected void init (&quad_t quad, 14862df5ad0Smrg &nichrome_t nichrome, 14962df5ad0Smrg void (&quad_t) callback) { 15062df5ad0Smrg Widget::init (&nichrome, &quad); 15162df5ad0Smrg quad.outline = outline; 15262df5ad0Smrg quad.draw = draw; 15362df5ad0Smrg quad.button = button; 15462df5ad0Smrg quad.motion = motion; 15562df5ad0Smrg quad.configure = configure; 156966b534aSmrg quad.natural = natural; 15762df5ad0Smrg quad.p = (point_t[4]) { 15862df5ad0Smrg { x = 0, y = 0 } ... 15962df5ad0Smrg }; 16062df5ad0Smrg quad.line_color = (rgba_color_t) { 16162df5ad0Smrg red = 1, green = 0, blue = 0, alpha = .5 16262df5ad0Smrg }; 16362df5ad0Smrg quad.line_width = 10; 16462df5ad0Smrg quad.corner_color = (rgba_color_t) { 16562df5ad0Smrg red = 0, green = 0, blue = 1, alpha = 0.75 16662df5ad0Smrg }; 16762df5ad0Smrg quad.corner_diameter = 20; 16862df5ad0Smrg quad.down = false; 169966b534aSmrg quad.active_corner = -1; 17062df5ad0Smrg quad.callback = callback; 17162df5ad0Smrg quad.started = false; 17262df5ad0Smrg } 17362df5ad0Smrg 17462df5ad0Smrg protected *quad_t new (&nichrome_t nichrome, void(&quad_t) callback) { 17562df5ad0Smrg quad_t quad; 17662df5ad0Smrg 17762df5ad0Smrg init (&quad, &nichrome, callback); 17862df5ad0Smrg return &quad; 17962df5ad0Smrg } 18062df5ad0Smrg } 18162df5ad0Smrg} 18262df5ad0Smrgimport Nichrome; 18362df5ad0Smrgimport Nichrome::Box; 18462df5ad0Smrgimport Nichrome::Label; 18562df5ad0Smrgimport Nichrome::Button; 18662df5ad0Smrgimport Nichrome::Quad; 18762df5ad0Smrg 18862df5ad0Smrgimport Cairo; 18962df5ad0Smrgtypedef real[3,3] m_t; 19062df5ad0Smrgtypedef point_t[4] q_t; 19162df5ad0Smrg 19262df5ad0Smrg/* 19362df5ad0Smrg * Ok, given an source quad and a dest rectangle, compute 19462df5ad0Smrg * a transform that maps the rectangle to q. That's easier 19562df5ad0Smrg * as the rectangle has some nice simple properties. Invert 19662df5ad0Smrg * the matrix to find the opposite mapping 19762df5ad0Smrg * 19862df5ad0Smrg * q0 q1 19962df5ad0Smrg * 20062df5ad0Smrg * q3 q2 20162df5ad0Smrg * 20262df5ad0Smrg * | m00 m01 m02 | 20362df5ad0Smrg * | m10 m11 m12 | 20462df5ad0Smrg * | m20 m21 m22 | 20562df5ad0Smrg * 20662df5ad0Smrg * m [ 0 0 1 ] = q[0] 20762df5ad0Smrg * 20862df5ad0Smrg * Set m22 to 1, and solve: 20962df5ad0Smrg * 21062df5ad0Smrg * | m02 , m12 , 1 | = | q0x, q0y, 1 | 21162df5ad0Smrg * 21262df5ad0Smrg * | m00 * w + q0x m10 * w + q0y | 21362df5ad0Smrg * | ------------- , ------------- , 1 | = | q1x, q1y, 1 | 21462df5ad0Smrg * | m20 * w + 1 m20 * w + 1 | 21562df5ad0Smrg 21662df5ad0Smrg * m00*w + q0x = q1x*(m20*w + 1) 21762df5ad0Smrg * m00 = m20*q1x + (q1x - q0x) / w; 21862df5ad0Smrg * 21962df5ad0Smrg * m10*w + q0y = q1y*(m20*w + 1) 22062df5ad0Smrg * m10 = m20*q1y + (q1y - q0y) / w; 22162df5ad0Smrg * 22262df5ad0Smrg * m01*h + q0x = q3x*(m21*h + 1) 22362df5ad0Smrg * m01 = m21*q3x + (q3x - q0x) / h; 22462df5ad0Smrg * 22562df5ad0Smrg * m11*h + q0y = q3y*(m21*h + 1) 22662df5ad0Smrg * m11 = m21*q3y + (q3y - q0y) / h 22762df5ad0Smrg * 22862df5ad0Smrg * m00*w + m01*h + q0x = q2x*(m20*w + m21*h + 1) 22962df5ad0Smrg * 23062df5ad0Smrg * m20*q1x*w + q1x - q0x + m21*q3x*h + q3x - q0x + q0x = m20*q2x*w + m21*q2x*h + q2x 23162df5ad0Smrg * 23262df5ad0Smrg * m20*q1x*w - m20*q2x*w = m21*q2x*h - m21*q3x*h + q2x - q1x + q0x - q3x + q0x - q0x 23362df5ad0Smrg * 23462df5ad0Smrg * m20*(q1x - q2x)*w = m21*(q2x - q3x)*h + q2x - q1x - q3x + q0x 23562df5ad0Smrg * 23662df5ad0Smrg * 23762df5ad0Smrg * m10*w + m11*h + q0y = q2y*(m20*w + m21*h + 1) 23862df5ad0Smrg * 23962df5ad0Smrg * m20*q1y*w + q1y - q0y + m21*q3y*h + q3y - q0y + q0y = m20*q2y*w + m21*q2y*h + q2y 24062df5ad0Smrg * 24162df5ad0Smrg * m20*q1y*w - m20*q2y*w = m21*q2y*h - m21*q3y*h + q2y - q1y + q0y - q3y + q0y - q0y 24262df5ad0Smrg * 24362df5ad0Smrg * m20*(q1y - q2y)*w = m21*(q2y - q3y)*h + q2y - q1y - q3y + q0y 24462df5ad0Smrg * 24562df5ad0Smrg * 24662df5ad0Smrg * m20*(q1x - q2x)*(q1y - q2y)*w = m21*(q2x - q3x)*(q1y - q2y)*h + (q2x - q1x - q3x + q0x)*(q1y - q2y) 24762df5ad0Smrg * 24862df5ad0Smrg * m20*(q1y - q2y)*(q1x - q2x)*w = m21*(q2y - q3y)*(q1x - q2x)*h + (q2y - q1y - q3y + q0y)*(q1x - q2x) 24962df5ad0Smrg * 25062df5ad0Smrg * 0 = m21*((q2x - q3x)*(q1y - q2y) - (q2y - q3y)*(q1x - q2x))*h + (stuff) 25162df5ad0Smrg * = m21 * a + b; 25262df5ad0Smrg * 25362df5ad0Smrg * m21 = -(stuff) / (other stuff) 25462df5ad0Smrg * 25562df5ad0Smrg * m20 = f(m21) 25662df5ad0Smrg * 25762df5ad0Smrg * m00 = f(m20) 25862df5ad0Smrg * m10 = f(m20) 25962df5ad0Smrg * 26062df5ad0Smrg * m01 = f(m21) 26162df5ad0Smrg * m11 = f(m21) 26262df5ad0Smrg * 26362df5ad0Smrg * done. 26462df5ad0Smrg */ 26562df5ad0Smrgm_t solve (q_t q, real w, real h) 26662df5ad0Smrg{ 26762df5ad0Smrg real q0x = q[0].x, q0y = q[0].y; 26862df5ad0Smrg real q1x = q[1].x, q1y = q[1].y; 26962df5ad0Smrg real q2x = q[2].x, q2y = q[2].y; 27062df5ad0Smrg real q3x = q[3].x, q3y = q[3].y; 27162df5ad0Smrg real m00, m01, m02; 27262df5ad0Smrg real m10, m11, m12; 27362df5ad0Smrg real m20, m21, m22; 27462df5ad0Smrg 27562df5ad0Smrg m02 = q0x; 27662df5ad0Smrg m12 = q0y; 27762df5ad0Smrg m22 = 1; 27862df5ad0Smrg 27962df5ad0Smrg real a = ((q2x - q3x)*(q1y - q2y) - (q2y - q3y)*(q1x - q2x)) * h; 28062df5ad0Smrg real b = (q2x - q1x - q3x + q0x) * (q1y - q2y) - (q2y - q1y - q3y + q0y) * (q1x - q2x); 28162df5ad0Smrg m21 = - b / a; 28262df5ad0Smrg 28362df5ad0Smrg if (q1x != q2x) 28462df5ad0Smrg m20 = (m21 * (q2x - q3x) * h + q2x - q1x - q3x + q0x) / ((q1x - q2x) * w); 28562df5ad0Smrg else 28662df5ad0Smrg m20 = (m21 * (q2y - q3y) * h + q2y - q1y - q3y + q0y) / ((q1y - q2y) * w); 28762df5ad0Smrg 28862df5ad0Smrg m00 = m20 * q1x + (q1x - q0x) / w; 28962df5ad0Smrg m10 = m20 * q1y + (q1y - q0y) / w; 29062df5ad0Smrg 29162df5ad0Smrg m01 = m21 * q3x + (q3x - q0x) / h; 29262df5ad0Smrg m11 = m21 * q3y + (q3y - q0y) / h; 29362df5ad0Smrg 29462df5ad0Smrg return (m_t) { 29562df5ad0Smrg { m00, m01, m02 }, 29662df5ad0Smrg { m10, m11, m12 }, 29762df5ad0Smrg { m20, m21, m22 } }; 29862df5ad0Smrg} 29962df5ad0Smrg 30062df5ad0Smrgm_t 30162df5ad0Smrginvert (m_t m) 30262df5ad0Smrg{ 30362df5ad0Smrg real det; 30462df5ad0Smrg int i, j; 30562df5ad0Smrg m_t r; 30662df5ad0Smrg 30762df5ad0Smrg static int[3] a = { 2, 2, 1 }; 30862df5ad0Smrg static int[3] b = { 1, 0, 0 }; 30962df5ad0Smrg 31062df5ad0Smrg det = 0; 31162df5ad0Smrg for (i = 0; i < 3; i++) { 31262df5ad0Smrg real p; 31362df5ad0Smrg int ai = a[i]; 31462df5ad0Smrg int bi = b[i]; 31562df5ad0Smrg p = m[i,0] * (m[ai,2] * m[bi,1] - m[ai,1] * m[bi,2]); 31662df5ad0Smrg if (i == 1) 31762df5ad0Smrg p = -p; 31862df5ad0Smrg det += p; 31962df5ad0Smrg } 32062df5ad0Smrg det = 1/det; 32162df5ad0Smrg for (j = 0; j < 3; j++) { 32262df5ad0Smrg for (i = 0; i < 3; i++) { 32362df5ad0Smrg real p; 32462df5ad0Smrg int ai = a[i]; 32562df5ad0Smrg int aj = a[j]; 32662df5ad0Smrg int bi = b[i]; 32762df5ad0Smrg int bj = b[j]; 32862df5ad0Smrg 32962df5ad0Smrg p = m[ai,aj] * m[bi,bj] - m[ai,bj] * m[bi,aj]; 33062df5ad0Smrg if (((i + j) & 1) != 0) 33162df5ad0Smrg p = -p; 33262df5ad0Smrg r[j,i] = det * p; 33362df5ad0Smrg } 33462df5ad0Smrg } 33562df5ad0Smrg return r; 33662df5ad0Smrg} 33762df5ad0Smrg 33862df5ad0Smrgm_t 33962df5ad0Smrgrescale (m_t m, real limit) 34062df5ad0Smrg{ 34162df5ad0Smrg real max = 0; 34262df5ad0Smrg for (int j = 0; j < 3; j++) 34362df5ad0Smrg for (int i = 0; i < 3; i++) 34462df5ad0Smrg if ((real v = abs (m[j,i])) > max) 34562df5ad0Smrg max = v; 34662df5ad0Smrg real scale = limit / max; 34762df5ad0Smrg for (int j = 0; j < 3; j++) 34862df5ad0Smrg for (int i = 0; i < 3; i++) 34962df5ad0Smrg m[j,i] *= scale; 35062df5ad0Smrg return m; 35162df5ad0Smrg 35262df5ad0Smrg} 35362df5ad0Smrg 35462df5ad0Smrgstring 35562df5ad0Smrgm_print (m_t m) 35662df5ad0Smrg{ 35762df5ad0Smrg /* 35862df5ad0Smrg return sprintf ("%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f", 35962df5ad0Smrg m[0,0],m[0,1],m[0,2], 36062df5ad0Smrg m[1,0],m[1,1],m[1,2], 36162df5ad0Smrg m[2,0],m[2,1],m[2,2]); 36262df5ad0Smrg */ 36362df5ad0Smrg return sprintf ("%v,%v,%v,%v,%v,%v,%v,%v,%v", 36462df5ad0Smrg m[0,0],m[0,1],m[0,2], 36562df5ad0Smrg m[1,0],m[1,1],m[1,2], 36662df5ad0Smrg m[2,0],m[2,1],m[2,2]); 36762df5ad0Smrg} 36862df5ad0Smrg 36962df5ad0Smrgint 37062df5ad0Smrgfixed (real x) 37162df5ad0Smrg{ 37262df5ad0Smrg return floor (x * 65536 + 0.5) & 0xffffffff; 37362df5ad0Smrg} 37462df5ad0Smrg 37562df5ad0Smrgvoid 37662df5ad0Smrgm_print_fix (m_t m) 37762df5ad0Smrg{ 37862df5ad0Smrg for (int i = 0; i < 3; i++) 37962df5ad0Smrg { 38062df5ad0Smrg printf (" { 0x%08x, 0x%08x, 0x%08x },\n", 38162df5ad0Smrg fixed (m[i,0]), fixed (m[i,1]), fixed (m[i,2])); 38262df5ad0Smrg } 38362df5ad0Smrg} 38462df5ad0Smrg 38562df5ad0Smrgstring 38662df5ad0Smrgm_row (m_t m, int row) 38762df5ad0Smrg{ 38862df5ad0Smrg return sprintf ("%10.5f %10.5f %10.5f", 38962df5ad0Smrg m[row,0],m[row,1],m[row,2]); 39062df5ad0Smrg} 39162df5ad0Smrg 39262df5ad0SmrgCairo::point_t[*] scale(Cairo::point_t[*] p, real w, real h) 39362df5ad0Smrg{ 39462df5ad0Smrg for (int i = 0; i < dim (p); i++) { 39562df5ad0Smrg p[i].x *= w; 39662df5ad0Smrg p[i].y *= h; 39762df5ad0Smrg } 39862df5ad0Smrg return p; 39962df5ad0Smrg} 40062df5ad0Smrg 40162df5ad0Smrgtypedef struct { 40262df5ad0Smrg string name; 40362df5ad0Smrg rect_t geometry; 40462df5ad0Smrg} output_t; 40562df5ad0Smrg 40662df5ad0Smrgautoload Process; 40762df5ad0Smrg 40862df5ad0Smrg 40962df5ad0Smrgoutput_t[*] get_outputs () { 41062df5ad0Smrg output_t[...] outputs = {}; 41162df5ad0Smrg twixt (file randr = Process::popen (Process::popen_direction.read, 41262df5ad0Smrg false, "xrandr", "xrandr"); 41362df5ad0Smrg File::close (randr)) 41462df5ad0Smrg { 41562df5ad0Smrg while (!File::end (randr)) { 41662df5ad0Smrg string[*] words = String::wordsplit (File::fgets (randr), " "); 41762df5ad0Smrg if (dim (words) >= 3 && words[1] == "connected" && 41862df5ad0Smrg File::sscanf (words[2], "%dx%d+%d+%d", 41962df5ad0Smrg &(int width), &(int height), 42062df5ad0Smrg &(int x), &(int y)) == 4) 42162df5ad0Smrg { 42262df5ad0Smrg outputs[dim(outputs)] = (output_t) { 42362df5ad0Smrg name = words[0], 42462df5ad0Smrg geometry = { 42562df5ad0Smrg x = x, y = y, width = width, height = height 42662df5ad0Smrg } 42762df5ad0Smrg }; 42862df5ad0Smrg } 42962df5ad0Smrg } 43062df5ad0Smrg } 43162df5ad0Smrg return outputs; 43262df5ad0Smrg} 43362df5ad0Smrg 43462df5ad0Smrgvoid main () 43562df5ad0Smrg{ 43662df5ad0Smrg m_t m = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }, m_i, m_r; 43762df5ad0Smrg bool m_available = true; 43862df5ad0Smrg output_t[*] outputs = get_outputs (); 43962df5ad0Smrg output_t target_output; 44062df5ad0Smrg 44162df5ad0Smrg if (dim (outputs) == 0) { 44262df5ad0Smrg File::fprintf (stderr, "%s: No enabled outputs\n", argv[0]); 44362df5ad0Smrg exit (1); 44462df5ad0Smrg } 44562df5ad0Smrg 44662df5ad0Smrg if (dim (argv) > 1) { 44762df5ad0Smrg int i; 44862df5ad0Smrg for (i = 0; i < dim (outputs); i++) 44962df5ad0Smrg if (argv[1] == outputs[i].name) { 45062df5ad0Smrg target_output = outputs[i]; 45162df5ad0Smrg break; 45262df5ad0Smrg } 45362df5ad0Smrg if (i == dim (outputs)) { 45462df5ad0Smrg File::fprintf (stderr, "%s: no enabled output \"%s\"\n", 45562df5ad0Smrg argv[0], argv[1]); 45662df5ad0Smrg exit (1); 45762df5ad0Smrg } 45862df5ad0Smrg } 45962df5ad0Smrg else 46062df5ad0Smrg target_output = outputs[0]; 46162df5ad0Smrg 46262df5ad0Smrg real target_width = target_output.geometry.width; 46362df5ad0Smrg real target_height = target_output.geometry.height; 46462df5ad0Smrg 46562df5ad0Smrg real screen_width = 0; 46662df5ad0Smrg real screen_height = 0; 46762df5ad0Smrg 46862df5ad0Smrg for (int i = 0; i < dim (outputs); i++) 46962df5ad0Smrg { 47062df5ad0Smrg screen_width = max (screen_width, 47162df5ad0Smrg outputs[i].geometry.x + 47262df5ad0Smrg outputs[i].geometry.width); 47362df5ad0Smrg screen_height = max (screen_height, 47462df5ad0Smrg outputs[i].geometry.y + 47562df5ad0Smrg outputs[i].geometry.height); 47662df5ad0Smrg } 47762df5ad0Smrg 47862df5ad0Smrg &nichrome_t nichrome = Nichrome::new ("Keystone Correction", 400, 350); 47962df5ad0Smrg 48062df5ad0Smrg (*label_t)[3] label; 48162df5ad0Smrg &label_t space = Label::new (&nichrome, ""); 48262df5ad0Smrg for (int i = 0; i < 3; i++) { 48362df5ad0Smrg label[i] = Label::new (&nichrome, "matrix"); 48462df5ad0Smrg label[i]->font = "sans-9"; 48562df5ad0Smrg } 48662df5ad0Smrg 48762df5ad0Smrg void callback (&quad_t quad) { 48862df5ad0Smrg real w = quad.geometry.width; 48962df5ad0Smrg real h = quad.geometry.height; 49062df5ad0Smrg string[3] text; 49162df5ad0Smrg try { 49262df5ad0Smrg m = solve (scale (quad.p, target_width / w, target_height / h), 49362df5ad0Smrg target_width, target_height); 49462df5ad0Smrg m_i = invert (m); 49562df5ad0Smrg m_r = rescale (m_i, 16384); 49662df5ad0Smrg for (int i = 0; i < 3; i++) 49762df5ad0Smrg text[i] = m_row (m_i,i); 49862df5ad0Smrg m_available = true; 49962df5ad0Smrg } catch divide_by_zero (real a, real b) { 50062df5ad0Smrg text = (string[3]) { "no solution", "" ... }; 50162df5ad0Smrg m_available = false; 50262df5ad0Smrg } 50362df5ad0Smrg for (int i = 0; i < 3; i++) 50462df5ad0Smrg Label::relabel (label[i], text[i]); 50562df5ad0Smrg } 50662df5ad0Smrg &quad_t quad = Quad::new (&nichrome, callback); 50762df5ad0Smrg 50862df5ad0Smrg void doit_func (&widget_t widget, bool state) 50962df5ad0Smrg { 51062df5ad0Smrg if (m_available) 51162df5ad0Smrg { 51262df5ad0Smrg Process::system ("xrandr", 51362df5ad0Smrg "xrandr", 51462df5ad0Smrg "--fb", 51562df5ad0Smrg sprintf ("%dx%d", screen_width, screen_height), 51662df5ad0Smrg "--output", 51762df5ad0Smrg target_output.name, 51862df5ad0Smrg "--transform", 51962df5ad0Smrg m_print (m_r)); 52062df5ad0Smrg } 52162df5ad0Smrg } 52262df5ad0Smrg 52362df5ad0Smrg &button_t doit = Button::new (&nichrome, "doit", doit_func); 52462df5ad0Smrg 52562df5ad0Smrg void show_func (&widget_t widget, bool state) 52662df5ad0Smrg { 52762df5ad0Smrg if (m_available) 52862df5ad0Smrg { 52962df5ad0Smrg printf ("normal: %s\n", m_print (m)); 53062df5ad0Smrg printf ("inverse: %s\n", m_print (m_i)); 53162df5ad0Smrg printf ("scaled: %s\n", m_print (m_r)); 53262df5ad0Smrg printf ("fixed:\n"); 53362df5ad0Smrg m_print_fix (m_i); 53462df5ad0Smrg } 53562df5ad0Smrg } 53662df5ad0Smrg 53762df5ad0Smrg &button_t show = Button::new (&nichrome, "show", show_func); 53862df5ad0Smrg &button_t quit = Button::new (&nichrome, "quit", 53962df5ad0Smrg void func (&widget_t w, bool state) { 54062df5ad0Smrg w.nichrome.running = false; 54162df5ad0Smrg }); 54262df5ad0Smrg 54362df5ad0Smrg &box_t hbox = Box::new (Box::dir_t.horizontal, 54462df5ad0Smrg Box::widget_item (&doit, 0), 54562df5ad0Smrg Box::widget_item (&show, 0), 54662df5ad0Smrg Box::widget_item (&quit, 0), 54762df5ad0Smrg Box::glue_item (1)); 54862df5ad0Smrg &box_t box = Box::new (Box::dir_t.vertical, 54962df5ad0Smrg Box::box_item (&hbox), 550966b534aSmrg Box::widget_item (label[0], 1, 0), 551966b534aSmrg Box::widget_item (label[1], 1, 0), 552966b534aSmrg Box::widget_item (label[2], 1, 0), 553966b534aSmrg Box::widget_item (&space, 1, 0), 55462df5ad0Smrg Box::widget_item (&quad, 1)); 55562df5ad0Smrg Nichrome::set_box (&nichrome, &box); 55662df5ad0Smrg Nichrome::main_loop (&nichrome); 55762df5ad0Smrg} 55862df5ad0Smrg 55962df5ad0Smrgmain (); 560