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
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
//! # processing-rs
//!
//! `processing-rs` is a crate that is designed to make graphics
//! programming as easy in Rust as it is in the popular Processing
//! environment, without sacrificing performance or easy access to
//! other parts of your system. It achieves this by essentially being
//! a convienence layer built on top of glium and glutin/glfw (either
//! can be chosen depending on your preference). It mostly mimics Processing,
//! but diverges in a few areas to either accomodate Rust's and glium's safety
//! features or to incorporate some ideas from libCinder and openFrameworks. For
//! instance, there are now `setup` and `draw` loops in `processing-rs`. This is
//! intended to enable a more flexible and modular workflow for graphics. In addition,
//! since `processing-rs` is essentialy a thin wrapper, you can use glium, glutin, and
//! raw OpenGL calls as you see fit. They should usually blend nicely with the
//! `processing-rs` calls and allow for more opportunities to create fun and
//! interesting displays.
//!
//! Typical usage follows a common pattern:
//!
//!     1. Open a screen with processing::Screen::new(). This will return a 
//!        Screen struct, which is the central struct for coordinating draw calls,
//!        managing shaders, and maintaining render state.
//!
//!     2. Pre-define a few shapes. This allows caching shape data on the CPU and GPU,
//!        giving some marginal speed boosts and allowing the possibility to store
//!        shapes in collections, such as Vectors. All shapes (Rect, Ellipse, etc.) in
//!        this crate implement the Shape trait, which defines a common interface for
//!        entities that can be drawn to a screen in a meaningful way.
//!
//!     3. Draw the shapes to the screen with screen.draw(). This is also where you will
//!        want to use commands like screen.fill() and screen.stroke() to change the
//!        colors of objects.
//!
//!     4. For any pattern drawn, you will also need to flip the framebuffers, so that
//!        the pattern is synchronized with your monitor. This is achieved by
//!        screen.reveal().
//!
//!     5. Use commands like screen.key_press() to get input from users, if needed.
//!
//!     6. Have fun! :-)
//!
//! Basically, all commands follow the same call conventions as those from Processing,
//! so you can also use the Processing reference as additional documentation and for
//! some basic examples of what you can do.
//!
//! Additionally, `processing-rs` has a number of features that are intended to make
//! it useful for psychological research, including color vision, material perception,
//! motion, etc. For example, it tries to enable a 10-bit framebuffer if possible, for
//! increased color fidelity, which is important in most color vision research. Besides
//! this, it also aims to make sure that frame draws are as precisely synchronized with 
//! the monitor refresh as possible. This works better with glutin than with glfw, and 
//! on Mac, a few Objective-C functions are called to give the program elevated status
//! for resources and to disable AppNap and such things while the program is running
//! (code taken from the helpful PsychToolbox). In addition, when the crate is built
//! on a Mac, it will also compile a C library (called `libpri`, short for "priority
//! library") that asks the operating system for some additional priorities. The crate
//! will automatically call the library throguh the C FFI at screen initialization. It
//! shouldn't interfere with normal operation of the operating system, so you can
//! probably just accept the default behaviour. With all of this, combined with a change
//! to a setting on Mac that allows one to quit Finder, one can achieve slightly better 
//! synchronization than PsychToolbox on Mac, satisfying Psychtoolbox's requirements for
//! good frame synchronization. However, this has only been tested on a Mac laptop with
//! 10.13.3 and an Intel graphics card.

#[macro_use]
extern crate glium;
extern crate gl;
extern crate nalgebra;
//extern crate rand;
extern crate image as image_ext;
extern crate owning_ref;

#[cfg(target_os = "macos")]
#[macro_use]
extern crate objc;
#[cfg(target_os = "macos")]
extern crate cocoa;
#[cfg(target_os = "macos")]
use cocoa::foundation::{NSProcessInfo, NSString};
#[cfg(target_os = "macos")]
use cocoa::base::nil;

use std::collections::HashMap;

use nalgebra::{Matrix4, Vector3, Unit};

#[cfg(not(feature = "glfw"))]
pub use glium::*;
#[cfg(not(feature = "glfw"))]
pub mod screen;

#[cfg(feature = "glfw")]
extern crate glfw;
#[cfg(feature = "glfw")]
pub mod glfwp5;
#[cfg(feature = "glfw")]
pub use glfwp5::screen;

#[macro_use]
pub mod shaders;
pub mod color;
pub mod shapes;
pub mod textures;
pub mod framebuffers;
pub mod transform;
pub mod rendering;
pub mod image;
pub mod errors;

#[cfg(not(feature = "glfw"))]
pub mod environment;
#[cfg(feature = "glfw")]
pub use glfwp5::environment;

#[cfg(not(feature = "glfw"))]
pub mod input;
#[cfg(feature = "glfw")]
pub use glfwp5::input;

#[cfg(not(feature = "glfw"))]
pub mod constants;
#[cfg(feature = "glfw")]
pub use glfwp5::constants;

pub use constants::{Key, MouseButton};

pub use image::load_image;

#[derive(Debug)]
pub struct GLmatStruct {
    pub currMatrix: Matrix4<f32>,
    matrixStack: Vec<Matrix4<f32>>,
}

pub struct FBtexs {
    fbtex: glium::texture::Texture2d,
    depthtexture: glium::texture::DepthTexture2d,
}

#[derive(Copy, Clone)]
pub struct DFBFDVertex {
    position: [f32; 2],
    texcoord: [f32; 2],
}

implement_vertex!(DFBFDVertex, position, texcoord);

#[cfg(not(feature = "glfw"))]
enum ScreenType {
    Window(glium::Display),
    Headless(glium::HeadlessRenderer),
}

/// This is essentially the central struct of `processing-rs`. It not only contains the
/// the display window returned by glutin, but it also has a number of elements that
/// maintain the render state and that manage a framebuffer for increased color
/// fidelity. Its internal elements are mostly private and an instance of a Screen
/// struct should be interacted with through the public functions provided in other
/// modules, such as the shapes, environment, or textures modules.
#[cfg(not(feature = "glfw"))]
pub struct Screen<'a> {
    FBTexture: glium::texture::Texture2d,
    fb_shape_buffer: glium::VertexBuffer<DFBFDVertex>,
    fb_index_buffer: glium::index::IndexBuffer<u16>,
    FBO: owning_ref::OwningHandle<Box<FBtexs>, Box<glium::framebuffer::SimpleFrameBuffer<'a>>>,
    display: ScreenType,
    events_loop: glutin::EventsLoop,
    draw_params: glium::draw_parameters::DrawParameters<'a>,
    pub matrices: GLmatStruct,
    bgCol: Vec<f32>,
    fillStuff: bool,
    fillCol: Vec<f32>,
    strokeStuff: bool,
    strokeCol: Vec<f32>,
    tintStuff: bool,
    tintCol: Vec<f32>,
    shader_bank: Vec<glium::program::Program>,
    drawTexture: bool,
    aspectRatio: f32,
    preserveAspectRatio: bool,
    fbSize: Vec<u32>,
    strokeWeight: f32,
    fontFace: String,
    textSize: f32,
    height: u32,
    width: u32,
    left: f32,
    right: f32,
    top: f32,
    bottom: f32,
    cMode: String,
    title: String,
    ellipseMode: String,
    rectMode: String,
    shapeMode: String,
    imageMode: String,
    frameRate: isize,
    frameCount: isize,
    fontsInitialized: bool,
    CurrShader: usize,
    currCursor: glium::glutin::MouseCursor,
    wrap: glium::uniforms::SamplerWrapFunction,
    AlternateShader: usize,
    CurrTexture: Option<glium::texture::Texture2d>,
    UsingAlternateShader: bool,
    GlslVersion: String,
    drew_points: bool,
    keypressed: Option<glutin::VirtualKeyCode>,
    mousepressed: Option<glutin::MouseButton>,
    mousereleased: Option<glutin::MouseButton>,
    mousepos: (f64, f64),
    headless: bool,
}

#[cfg(feature = "glfw")]
use std::sync::mpsc::Receiver;
#[cfg(feature = "glfw")]
use glfwp5::backend::Display;
#[cfg(feature = "glfw")]
enum ScreenType {
    Window(Display),
    Headless(Display),
}

/// This is essentially the central struct of `processing-rs`. It not only contains the
/// the display window returned by glutin, but it also has a number of elements that
/// maintain the render state and that manage a framebuffer for increased color
/// fidelity. Its internal elements are mostly private and an instance of a Screen
/// struct should be interacted with through the public functions provided in other
/// modules, such as the shapes, environment, or textures modules.
#[cfg(feature = "glfw")]
pub struct Screen<'a> {
    FBTexture: glium::texture::Texture2d,
    fb_shape_buffer: glium::VertexBuffer<DFBFDVertex>,
    fb_index_buffer: glium::index::IndexBuffer<u16>,
    FBO: owning_ref::OwningHandle<Box<FBtexs>, Box<glium::framebuffer::SimpleFrameBuffer<'a>>>,
    display: ScreenType,
    glfw: glfw::Glfw,
    events_loop: Receiver<(f64, glfw::WindowEvent)>,
    draw_params: glium::draw_parameters::DrawParameters<'a>,
    pub matrices: GLmatStruct,
    bgCol: Vec<f32>,
    fillStuff: bool,
    fillCol: Vec<f32>,
    strokeStuff: bool,
    strokeCol: Vec<f32>,
    tintStuff: bool,
    tintCol: Vec<f32>,
    shader_bank: Vec<glium::program::Program>,
    drawTexture: bool,
    aspectRatio: f32,
    preserveAspectRatio: bool,
    fbSize: Vec<u32>,
    strokeWeight: f32,
    fontFace: String,
    textSize: f32,
    height: u32,
    width: u32,
    left: f32,
    right: f32,
    top: f32,
    bottom: f32,
    cMode: String,
    title: String,
    ellipseMode: String,
    rectMode: String,
    shapeMode: String,
    imageMode: String,
    frameRate: isize,
    frameCount: isize,
    fontsInitialized: bool,
    CurrShader: usize,
    currCursor: glfw::StandardCursor,
    wrap: glium::uniforms::SamplerWrapFunction,
    AlternateShader: usize,
    CurrTexture: Option<glium::texture::Texture2d>,
    UsingAlternateShader: bool,
    GlslVersion: String,
    drew_points: bool,
    keypressed: Option<glfw::Key>,
    mousepressed: Option<glfw::MouseButton>,
    mousereleased: Option<glfw::MouseButton>,
    mousepos: (f64, f64),
    headless: bool,
}

// #[derive(Default)]
// struct vertexStruct {
// shapeVertices: Vec<f32>,
// textureCoords: Vec<f32>,
// vertexStride: isize,
// nVertices: isize,
// shapeType: u32,
// }

#[cfg(target_os = "macos")]
#[link(name = "pri")]
extern "C" {
    fn setMaxPriority();
}

#[cfg(target_os = "macos")]
fn mac_priority() {
    // Prevent display from sleeping/powering down, prevent system
    // from sleeping, prevent sudden termination for any reason:
    let NSActivityIdleDisplaySleepDisabled = (1u64 << 40);
    let NSActivityIdleSystemSleepDisabled = (1u64 << 20);
    let NSActivitySuddenTerminationDisabled = (1u64 << 14);
    let NSActivityAutomaticTerminationDisabled = (1u64 << 15);
    let NSActivityUserInitiated = (0x00FFFFFFu64 | NSActivityIdleSystemSleepDisabled);
    let NSActivityLatencyCritical = 0xFF00000000u64;

    let options = NSActivityIdleDisplaySleepDisabled | NSActivityIdleSystemSleepDisabled |
        NSActivitySuddenTerminationDisabled |
        NSActivityAutomaticTerminationDisabled;
    let options = options | NSActivityUserInitiated | NSActivityLatencyCritical;

    unsafe {
        let pinfo = NSProcessInfo::processInfo(nil);
        let s = NSString::alloc(nil).init_str("timing");
        msg_send![pinfo, beginActivityWithOptions:options reason:s];

        setMaxPriority();
    }
}