1 // ----------------------------------------------------------------------------
2 //
3 //  Copyright (C) 2008-2015 Fons Adriaensen <fons@linuxaudio.org>
4 //
5 //  This program is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation; either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 //
18 // ----------------------------------------------------------------------------
19 
20 
21 #include <cairo/cairo.h>
22 #include <cairo/cairo-xlib.h>
23 #include <math.h>
24 #include "rotary.h"
25 
26 
27 cairo_t         *RotaryCtl::_cairotype = 0;
28 cairo_surface_t *RotaryCtl::_cairosurf = 0;
29 
30 
31 int RotaryCtl::_wb_up = 4;
32 int RotaryCtl::_wb_dn = 5;
33 int RotaryCtl::_keymod = 0;
34 int RotaryCtl::_button = 0;
35 int RotaryCtl::_rcount = 0;
36 int RotaryCtl::_rx = 0;
37 int RotaryCtl::_ry = 0;
38 
39 
RotaryCtl(X_window * parent,X_callback * cbobj,int cbind,RotaryGeom * rgeom,int xp,int yp)40 RotaryCtl::RotaryCtl (X_window     *parent,
41                       X_callback   *cbobj,
42 		      int           cbind,
43                       RotaryGeom   *rgeom,
44 		      int           xp,
45                       int           yp) :
46 
47     X_window (parent,
48               rgeom->_x0 + xp, rgeom->_y0 + yp,
49               rgeom->_dx, rgeom->_dy,
50               rgeom->_backg->pixel),
51     _cbobj (cbobj),
52     _cbind (cbind),
53     _rgeom (rgeom),
54     _state (0),
55     _count (0),
56     _value (0),
57     _angle (0)
58 {
59     x_add_events (  ExposureMask
60                   | Button1MotionMask | ButtonPressMask | ButtonReleaseMask);
61 }
62 
63 
~RotaryCtl(void)64 RotaryCtl::~RotaryCtl (void)
65 {
66 }
67 
68 
init(X_display * disp)69 void RotaryCtl::init (X_display *disp)
70 {
71     _cairosurf = cairo_xlib_surface_create (disp->dpy (), 0, disp->dvi (), 50, 50);
72     _cairotype = cairo_create (_cairosurf);
73 }
74 
75 
fini(void)76 void RotaryCtl::fini (void)
77 {
78     cairo_destroy (_cairotype);
79     cairo_surface_destroy (_cairosurf);
80 }
81 
82 
handle_event(XEvent * E)83 void RotaryCtl::handle_event (XEvent *E)
84 {
85     switch (E->type)
86     {
87     case Expose:
88 	render ();
89 	break;
90 
91     case ButtonPress:
92 	bpress ((XButtonEvent *) E);
93 	break;
94 
95     case ButtonRelease:
96 	brelse ((XButtonEvent *) E);
97 	break;
98 
99     case MotionNotify:
100 	motion ((XMotionEvent *) E);
101 	break;
102 
103     default:
104 	fprintf (stderr, "RotaryCtl: event %d\n", E->type );
105     }
106 }
107 
108 
bpress(XButtonEvent * E)109 void RotaryCtl::bpress (XButtonEvent *E)
110 {
111     int    r = 0;
112     double d;
113 
114     d = hypot (E->x - _rgeom->_xref, E->y - _rgeom->_yref);
115     if (d > _rgeom->_rad + 3) return;
116     _keymod  = E->state;
117     if (E->button < 4)
118     {
119 	_rx = E->x;
120 	_ry = E->y;
121 	_button = E->button;
122         r = handle_button ();
123 	_rcount = _count;
124     }
125     else if (_button) return;
126     else if ((int)E->button == _wb_up)
127     {
128         r = handle_mwheel (1);
129     }
130     else if ((int)E->button == _wb_dn)
131     {
132         r = handle_mwheel (-1);
133     }
134     if (r)
135     {
136         callback (r);
137 	render ();
138     }
139 }
140 
141 
brelse(XButtonEvent * E)142 void RotaryCtl::brelse (XButtonEvent *E)
143 {
144     if (_button == (int)E->button)
145     {
146 	_button = 0;
147 	callback (RELSE);
148     }
149 }
150 
151 
motion(XMotionEvent * E)152 void RotaryCtl::motion (XMotionEvent *E)
153 {
154     int dx, dy, r;
155 
156     if (_button)
157     {
158         _keymod = E->state;
159         dx = E->x - _rx;
160         dy = E->y - _ry;
161         r = handle_motion (dx, dy);
162 	if (r)
163 	{
164             callback (r);
165 	    render ();
166 	}
167     }
168 }
169 
170 
set_state(int s)171 void RotaryCtl::set_state (int s)
172 {
173     if (_state != s)
174     {
175         _state = s;
176         render ();
177     }
178 }
179 
180 
render(void)181 void RotaryCtl::render (void)
182 {
183     double  a, c, r, x, y;
184 
185     // A very weird bugfix. Without this, the cairo line
186     // draw below fails if the line is exactly horizontal
187     // or vertical. No amount of XFlush(), cairo_surface_flush()
188     // or cairo_surface_mark_dirty() seems to help, but this
189     // XDrawline does.
190     XDrawLine (dpy (), win (), dgc (), 0, 0, 0, 0);
191 
192     XPutImage (dpy (), win (), dgc (), _rgeom->_image [_state],
193                _rgeom->_x0, _rgeom->_y0, 0, 0, _rgeom->_dx, _rgeom->_dy);
194     XFlush (dpy ());
195     cairo_xlib_surface_set_drawable (_cairosurf, win(),_rgeom->_dx, _rgeom->_dy);
196     c = _rgeom->_lncol [_state] ? 1.0 : 0.0;
197     a = _angle * M_PI / 180;
198     r = _rgeom->_rad;
199     x = _rgeom->_xref;
200     y = _rgeom->_yref;
201     cairo_new_path (_cairotype);
202     cairo_move_to (_cairotype, x, y);
203     x += r * sin (a);
204     y -= r * cos (a);
205     cairo_line_to (_cairotype, x, y);
206     cairo_set_source_rgb (_cairotype, c, c, c);
207     cairo_set_line_width (_cairotype, c ? 2.2 : 1.8);
208     cairo_stroke (_cairotype);
209     cairo_surface_flush (_cairosurf);
210 }
211 
212 
213