Commit f06b625998d4e914e63ec854a24b47e52357cd37
1 parent
45d1af9c
Add first simple triable animation based on fractionals
Showing
5 changed files
with
301 additions
and
2 deletions
@@ -6,3 +6,7 @@ edition = "2018" | @@ -6,3 +6,7 @@ edition = "2018" | ||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | lazy_static = "1.4.0" | 8 | lazy_static = "1.4.0" |
9 | +libc = "0.2" | ||
10 | +gl = "0.5.2" | ||
11 | +x11 = { version = "2.3", features = ["glx"] } | ||
12 | +xcb = { version = "0.8", features = ["dri2", "randr", "thread", "xlib_xcb", "shm"] } |
@@ -20,13 +20,19 @@ | @@ -20,13 +20,19 @@ | ||
20 | // | 20 | // |
21 | use std::cmp; | 21 | use std::cmp; |
22 | use std::fmt::{Formatter, Display, Result}; | 22 | use std::fmt::{Formatter, Display, Result}; |
23 | +use std::sync::mpsc; | ||
23 | 24 | ||
24 | pub trait Easel { | 25 | pub trait Easel { |
25 | - fn canvas(self) -> dyn Canvas; | 26 | + //fn canvas(&mut self, width :u16, height :u16) -> Option<&dyn Canvas>; |
26 | } | 27 | } |
27 | 28 | ||
28 | pub trait Canvas { | 29 | pub trait Canvas { |
29 | - fn draw(self, c :&dyn Drawable, ofs :Coordinate); | 30 | + fn init_events(&self); |
31 | + fn start_events(&self, tx :mpsc::Sender<i32>); | ||
32 | + | ||
33 | + fn clear(&mut self); | ||
34 | + fn draw(&mut self, c :&dyn Drawable, ofs :Coordinate); | ||
35 | + fn show(&self); | ||
30 | } | 36 | } |
31 | 37 | ||
32 | pub trait Drawable { | 38 | pub trait Drawable { |
@@ -28,6 +28,7 @@ pub mod fractional; | @@ -28,6 +28,7 @@ pub mod fractional; | ||
28 | pub mod transform; | 28 | pub mod transform; |
29 | pub mod trigonometry; | 29 | pub mod trigonometry; |
30 | pub mod vector; | 30 | pub mod vector; |
31 | +pub mod xcb; | ||
31 | 32 | ||
32 | use fractional::Fractional; | 33 | use fractional::Fractional; |
33 | use vector::Vector; | 34 | use vector::Vector; |
@@ -23,6 +23,9 @@ use std::f64::consts::PI as FPI; | @@ -23,6 +23,9 @@ use std::f64::consts::PI as FPI; | ||
23 | use std::fmt::Display; | 23 | use std::fmt::Display; |
24 | use std::num::TryFromIntError; | 24 | use std::num::TryFromIntError; |
25 | use std::ops::{Add,Sub,Neg,Mul,Div}; | 25 | use std::ops::{Add,Sub,Neg,Mul,Div}; |
26 | +use std::sync::mpsc; | ||
27 | +use std::time; | ||
28 | +use std::thread; | ||
26 | 29 | ||
27 | use fractional::continuous::Continuous; | 30 | use fractional::continuous::Continuous; |
28 | use fractional::easel::{ Coordinate, Coordinates, Drawable, Line, Polyline | 31 | use fractional::easel::{ Coordinate, Coordinates, Drawable, Line, Polyline |
@@ -32,6 +35,9 @@ use fractional::trigonometry::Trig; | @@ -32,6 +35,9 @@ use fractional::trigonometry::Trig; | ||
32 | use fractional::vector::{Vector}; | 35 | use fractional::vector::{Vector}; |
33 | use fractional::transform::{TMatrix, translate, rotate_x, rotate_y, rotate_z, rotate_v}; | 36 | use fractional::transform::{TMatrix, translate, rotate_x, rotate_y, rotate_z, rotate_v}; |
34 | 37 | ||
38 | +use fractional::xcb::XcbEasel; | ||
39 | +use fractional::easel::Canvas; | ||
40 | + | ||
35 | fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> { | 41 | fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> { |
36 | let r = v.iter().fold(0, |acc, x| acc + x); | 42 | let r = v.iter().fold(0, |acc, x| acc + x); |
37 | let l = i64::try_from(v.len())?; | 43 | let l = i64::try_from(v.len())?; |
@@ -325,4 +331,54 @@ fn main() { | @@ -325,4 +331,54 @@ fn main() { | ||
325 | _transform2(); | 331 | _transform2(); |
326 | println!(); | 332 | println!(); |
327 | _line(); | 333 | _line(); |
334 | + | ||
335 | + let xcb = XcbEasel::new().unwrap(); | ||
336 | + let mut canvas = xcb.canvas(151, 151).unwrap(); | ||
337 | + | ||
338 | + canvas.set_title("Something..."); | ||
339 | + canvas.init_events(); | ||
340 | + | ||
341 | + let (tx, rx) = mpsc::channel(); | ||
342 | + let tx1 = mpsc::Sender::clone(&tx); | ||
343 | + canvas.start_events(tx); | ||
344 | + | ||
345 | + let i = Vector(Fractional( 0,1), Fractional(-35,1), Fractional(0,1)); | ||
346 | + let j = Vector(Fractional( 30,1), Fractional( 17,1), Fractional(0,1)); | ||
347 | + let k = Vector(Fractional(-30,1), Fractional( 17,1), Fractional(0,1)); | ||
348 | + | ||
349 | + fn to_i32(x :Fractional) -> i32 { | ||
350 | + let Fractional(n, d) = x; | ||
351 | + (n / d + if (n % d).abs() < (n / 2).abs() { 0 } else { 1 }) as i32 | ||
352 | + } | ||
353 | + | ||
354 | + thread::spawn(move || { | ||
355 | + loop { | ||
356 | + tx1.send(0).unwrap(); | ||
357 | + thread::sleep(time::Duration::from_millis(10)); | ||
358 | + } | ||
359 | + }); | ||
360 | + | ||
361 | + let mut deg :i32 = 0; | ||
362 | + | ||
363 | + for x in rx { | ||
364 | + match x { | ||
365 | + 1 => break, | ||
366 | + _ => { | ||
367 | + let rot :TMatrix<Fractional> = rotate_z(deg); | ||
368 | + let Vector(ix, iy, _) = rot.apply(&i); | ||
369 | + let Vector(jx, jy, _) = rot.apply(&j); | ||
370 | + let Vector(kx, ky, _) = rot.apply(&k); | ||
371 | + | ||
372 | + let pg = Polygon( | ||
373 | + Coordinates(vec!( Coordinate(to_i32(ix), to_i32(iy)) | ||
374 | + , Coordinate(to_i32(jx), to_i32(jy)) | ||
375 | + , Coordinate(to_i32(kx), to_i32(ky)) ))); | ||
376 | + | ||
377 | + canvas.clear(); | ||
378 | + canvas.draw(&pg, Coordinate(75,75)); | ||
379 | + canvas.show(); | ||
380 | + deg = (deg + 3) % 359; | ||
381 | + }, | ||
382 | + } | ||
383 | + } | ||
328 | } | 384 | } |
fractional/src/xcb.rs
0 → 100644
1 | +// | ||
2 | +// XCB implementation for drawing some stuff... | ||
3 | +// | ||
4 | +// Georg Hopp <georg@steffers.org> | ||
5 | +// | ||
6 | +// Copyright © 2019 Georg Hopp | ||
7 | +// | ||
8 | +// This program is free software: you can redistribute it and/or modify | ||
9 | +// it under the terms of the GNU General Public License as published by | ||
10 | +// the Free Software Foundation, either version 3 of the License, or | ||
11 | +// (at your option) any later version. | ||
12 | +// | ||
13 | +// This program is distributed in the hope that it will be useful, | ||
14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | +// GNU General Public License for more details. | ||
17 | +// | ||
18 | +// You should have received a copy of the GNU General Public License | ||
19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | +// | ||
21 | +extern crate xcb; | ||
22 | + | ||
23 | +use std::sync::Arc; | ||
24 | +use std::ptr; //::{null, null_mut}; | ||
25 | +use std::thread; | ||
26 | +use std::sync::mpsc; | ||
27 | + | ||
28 | +use crate::easel::{Easel, Canvas, Drawable, Coordinate, Coordinates}; | ||
29 | + | ||
30 | +#[derive(Clone)] | ||
31 | +pub struct XcbEasel (Arc<xcb::Connection>, i32); | ||
32 | + | ||
33 | +pub struct XcbCanvas<'a> { conn :Arc<xcb::Connection> | ||
34 | + , width :u16 | ||
35 | + , height :u16 | ||
36 | + , window :u32 | ||
37 | + , pixmap :u32 | ||
38 | + , gc :u32 | ||
39 | + , shm :&'a mut [u32] } | ||
40 | + | ||
41 | +impl XcbEasel { | ||
42 | + pub fn new() -> Result<XcbEasel, xcb::ConnError> { | ||
43 | + let (conn, num) = xcb::Connection::connect(None)?; | ||
44 | + | ||
45 | + Ok(XcbEasel(Arc::new(conn), num)) | ||
46 | + } | ||
47 | + | ||
48 | + pub fn setup(&self) -> xcb::Setup { | ||
49 | + let XcbEasel(conn, _) = self; | ||
50 | + conn.get_setup() | ||
51 | + } | ||
52 | + | ||
53 | + pub fn screen(&self) -> Option<xcb::Screen> { | ||
54 | + let XcbEasel(_, num) = self; | ||
55 | + self.setup().roots().nth(*num as usize) | ||
56 | + } | ||
57 | + | ||
58 | + pub fn canvas(&self, width :u16, height :u16) -> Option<XcbCanvas> { | ||
59 | + let Self(conn, _) = self; | ||
60 | + let conn = conn.clone(); | ||
61 | + let screen = match self.screen() { | ||
62 | + None => return None, | ||
63 | + Some(screen) => screen, | ||
64 | + }; | ||
65 | + | ||
66 | + let shmseg = conn.generate_id(); | ||
67 | + let gc = conn.generate_id(); | ||
68 | + let pixmap = conn.generate_id(); | ||
69 | + let window = conn.generate_id(); | ||
70 | + | ||
71 | + xcb::create_window( &conn, xcb::COPY_FROM_PARENT as u8, window | ||
72 | + , screen.root(), 0, 0, width, width, 0 | ||
73 | + , xcb::WINDOW_CLASS_INPUT_OUTPUT as u16 | ||
74 | + , screen.root_visual() | ||
75 | + , &[(xcb::CW_BACK_PIXEL, screen.white_pixel())] ); | ||
76 | + | ||
77 | + xcb::create_gc( &conn, gc, screen.root() | ||
78 | + , &[ (xcb::GC_FOREGROUND, screen.black_pixel()) | ||
79 | + , (xcb::GC_GRAPHICS_EXPOSURES, 0) ] ); | ||
80 | + | ||
81 | + let (shmid, shm) = getshm((width * height) as usize); | ||
82 | + xcb::shm::attach(&conn, shmseg, shmid as u32, false); | ||
83 | + unsafe { libc::shmctl(shmid, libc::IPC_RMID, ptr::null_mut()); } | ||
84 | + | ||
85 | + xcb::shm::create_pixmap( &conn, pixmap, window, width, height | ||
86 | + , screen.root_depth(), shmseg, 0 ); | ||
87 | + | ||
88 | + xcb::map_window(&conn, window); | ||
89 | + conn.flush(); | ||
90 | + | ||
91 | + Some(XcbCanvas{ conn: conn | ||
92 | + , width: width | ||
93 | + , height: height | ||
94 | + , window: window | ||
95 | + , pixmap: pixmap | ||
96 | + , gc: gc | ||
97 | + , shm: shm } ) | ||
98 | + } | ||
99 | +} | ||
100 | + | ||
101 | +impl<'a> XcbCanvas<'a> { | ||
102 | + pub fn set_title(&self, title :&str) { | ||
103 | + let c = xcb::change_property_checked( &self.conn | ||
104 | + , xcb::PROP_MODE_REPLACE as u8 | ||
105 | + , self.window | ||
106 | + , xcb::ATOM_WM_NAME | ||
107 | + , xcb::ATOM_STRING | ||
108 | + , 8 | ||
109 | + , title.as_bytes() ); | ||
110 | + if self.conn.has_error().is_err() || c.request_check().is_err() { | ||
111 | + println!("Error setting title"); | ||
112 | + } | ||
113 | + } | ||
114 | +} | ||
115 | + | ||
116 | +fn getshm<'a>(size :usize) -> (i32, &'a mut [u32]) { | ||
117 | + use std::slice::from_raw_parts_mut; | ||
118 | + | ||
119 | + unsafe { | ||
120 | + let id = libc::shmget( libc::IPC_PRIVATE | ||
121 | + , size * 4 | ||
122 | + , libc::IPC_CREAT | 0o744 ); | ||
123 | + let ptr = libc::shmat(id, ptr::null(), 0); | ||
124 | + (id as i32, from_raw_parts_mut(ptr as *mut u32, size)) | ||
125 | + } | ||
126 | +} | ||
127 | + | ||
128 | +impl Easel for XcbEasel {} | ||
129 | + | ||
130 | +impl<'a> Canvas for XcbCanvas<'a> { | ||
131 | + fn init_events(&self) { | ||
132 | + let mask = [( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | ||
133 | + | xcb::EVENT_MASK_KEY_PRESS | ||
134 | + | xcb::EVENT_MASK_STRUCTURE_NOTIFY | ||
135 | + | xcb::EVENT_MASK_PROPERTY_CHANGE )]; | ||
136 | + xcb::change_window_attributes(&self.conn, self.window, &mask); | ||
137 | + self.conn.flush(); | ||
138 | + } | ||
139 | + | ||
140 | + fn start_events(&self, tx :mpsc::Sender<i32>) { | ||
141 | + let conn = self.conn.clone(); | ||
142 | + let window = self.window; | ||
143 | + let pixmap = self.pixmap; | ||
144 | + let gc = self.gc; | ||
145 | + let width = self.width; | ||
146 | + let height = self.height; | ||
147 | + | ||
148 | + thread::spawn(move || { | ||
149 | + loop { | ||
150 | + let event = conn.wait_for_event(); | ||
151 | + | ||
152 | + match event { | ||
153 | + None => break, | ||
154 | + Some(event) => { | ||
155 | + match event.response_type() & !0x80 { | ||
156 | + xcb::PROPERTY_NOTIFY => { | ||
157 | + let prop_notify :&xcb::PropertyNotifyEvent | ||
158 | + = unsafe { xcb::cast_event(&event) }; | ||
159 | + | ||
160 | + if prop_notify.atom() == xcb::ATOM_WM_NAME { | ||
161 | + // retrieving title | ||
162 | + let cookie | ||
163 | + = xcb::get_property( &conn | ||
164 | + , false | ||
165 | + , window | ||
166 | + , xcb::ATOM_WM_NAME | ||
167 | + , xcb::ATOM_STRING | ||
168 | + , 0, 1024 ); | ||
169 | + | ||
170 | + if let Ok(reply) = cookie.get_reply() { | ||
171 | + let r = reply.value(); | ||
172 | + let r = std::str::from_utf8(r).unwrap(); | ||
173 | + | ||
174 | + println!("title changed to: {}", r); | ||
175 | + } | ||
176 | + } | ||
177 | + }, | ||
178 | + | ||
179 | + xcb::EXPOSE => { | ||
180 | + xcb::copy_area( &conn, pixmap, window, gc | ||
181 | + , 0, 0, 0, 0 | ||
182 | + , width, height ); | ||
183 | + conn.flush(); | ||
184 | + }, | ||
185 | + | ||
186 | + xcb::KEY_PRESS => { | ||
187 | + let key_press: &xcb::KeyPressEvent | ||
188 | + = unsafe { xcb::cast_event(&event) }; | ||
189 | + | ||
190 | + println!( "Key '{}' pressed" | ||
191 | + , key_press.detail() ); | ||
192 | + | ||
193 | + // Q (on qwerty) | ||
194 | + if key_press.detail() == 0x18 { | ||
195 | + tx.send(1).unwrap(); | ||
196 | + break; | ||
197 | + } | ||
198 | + }, | ||
199 | + | ||
200 | + _ => {}, | ||
201 | + } | ||
202 | + }, | ||
203 | + } | ||
204 | + } | ||
205 | + }); | ||
206 | + } | ||
207 | + | ||
208 | + fn clear(&mut self) { | ||
209 | + unsafe { | ||
210 | + let ptr = self.shm.as_mut_ptr(); | ||
211 | + ptr::write_bytes( ptr, 0 | ||
212 | + , self.width as usize * self.height as usize); | ||
213 | + } | ||
214 | + } | ||
215 | + | ||
216 | + fn draw(&mut self, d :&dyn Drawable, ofs :Coordinate) { | ||
217 | + let Coordinates(c) = d.plot(); | ||
218 | + let Coordinate(xofs, yofs) = ofs; | ||
219 | + | ||
220 | + for Coordinate(x, y) in c { | ||
221 | + let idx :usize = ((y+yofs)*(self.width as i32)+x+xofs) as usize; | ||
222 | + self.shm[idx] = 0xFFFFFF; | ||
223 | + } | ||
224 | + } | ||
225 | + | ||
226 | + fn show(&self) { | ||
227 | + xcb::copy_area( &self.conn, self.pixmap, self.window, self.gc | ||
228 | + , 0, 0, 0, 0 | ||
229 | + , self.width, self.height ); | ||
230 | + self.conn.flush(); | ||
231 | + } | ||
232 | +} |
Please
register
or
login
to post a comment