main.rs 11.8 KB
Newer Older
1
2
3
4
#![deny(warnings)]
#![feature(const_fn)]

extern crate orbclient;
Jeremy Soller's avatar
Jeremy Soller committed
5
6
extern crate orbimage;
extern crate orbfont;
AleVul's avatar
AleVul committed
7
extern crate redox_users;
8

Jeremy Soller's avatar
Jeremy Soller committed
9
use std::{env, str};
10
11
use std::process::Command;

Jeremy Soller's avatar
Jeremy Soller committed
12
use orbclient::{Color, EventOption, Renderer, Window, WindowFlag};
13
use orbfont::Font;
Jeremy Soller's avatar
Jeremy Soller committed
14
use orbimage::Image;
SamwiseFilmore's avatar
SamwiseFilmore committed
15
use redox_users::{AllUsers};
16

Jeremy Soller's avatar
Jeremy Soller committed
17
18
19
20
21
22
23
24
25
26
27
#[derive(Clone, Copy)]
enum BackgroundMode {
    /// Do not resize the image, just center it
    Center,
    /// Resize the image to the display size
    Fill,
    /// Resize the image - keeping its aspect ratio, and fit it to the display with blank space
    Scale,
    /// Resize the image - keeping its aspect ratio, and crop to remove all blank space
    Zoom,
}
Jeremy Soller's avatar
Jeremy Soller committed
28

Jeremy Soller's avatar
Jeremy Soller committed
29
30
31
32
33
34
35
36
37
38
impl BackgroundMode {
    fn from_str(string: &str) -> BackgroundMode {
        match string {
            "fill" => BackgroundMode::Fill,
            "scale" => BackgroundMode::Scale,
            "zoom" => BackgroundMode::Zoom,
            _ => BackgroundMode::Center
        }
    }
}
Jeremy Soller's avatar
Jeremy Soller committed
39

Jeremy Soller's avatar
Jeremy Soller committed
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
fn find_scale(image: &Image, mode: BackgroundMode, display_width: u32, display_height: u32) -> (u32, u32) {
    match mode {
        BackgroundMode::Center => {
            (image.width(), image.height())
        },
        BackgroundMode::Fill => {
            (display_width, display_height)
        },
        BackgroundMode::Scale => {
            let d_w = display_width as f64;
            let d_h = display_height as f64;
            let i_w = image.width() as f64;
            let i_h = image.height() as f64;

            let scale = if d_w / d_h > i_w / i_h {
                d_h / i_h
            } else {
                d_w / i_w
            };

            ((i_w * scale) as u32, (i_h * scale) as u32)
        },
        BackgroundMode::Zoom => {
            let d_w = display_width as f64;
            let d_h = display_height as f64;
            let i_w = image.width() as f64;
            let i_h = image.height() as f64;

            let scale = if d_w / d_h < i_w / i_h {
                d_h / i_h
            } else {
                d_w / i_w
            };

            ((i_w * scale) as u32, (i_h * scale) as u32)
        }
    }
}
78

79
fn login_command(username: &str, pass: &str, launcher_cmd: &str, launcher_args: &[String]) -> Option<Command> {
SamwiseFilmore's avatar
SamwiseFilmore committed
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    
    let sys_users = match AllUsers::new() {
        Ok(users) => users,
        // Not maybe the best thing to do...
        Err(_) => {
            return None;
        }
    };
    
    match sys_users.get_by_name(&username) {
        Some(user) => if user.verify_passwd(&pass) {
            let mut command = user.login_cmd(&launcher_cmd);
            for arg in launcher_args.iter() {
                command.arg(&arg);
            }

            Some(command)
97
98
        } else {
            None
99
        },
SamwiseFilmore's avatar
SamwiseFilmore committed
100
        None => None
Jeremy Soller's avatar
Jeremy Soller committed
101
102
    }
}
Jeremy Soller's avatar
Jeremy Soller committed
103

104
fn login_window(launcher_cmd: &str, launcher_args: &[String], font: &Font, image: &Image, image_mode: BackgroundMode) -> Option<Command> {
Jeremy Soller's avatar
Jeremy Soller committed
105
106
107
108
    let (display_width, display_height) = orbclient::get_display_size().expect("orblogin: failed to get display size");

    let mut window = Window::new_flags(
        0, 0, display_width, display_height, "",
Jeremy Soller's avatar
Jeremy Soller committed
109
        &[WindowFlag::Unclosable]
Jeremy Soller's avatar
Jeremy Soller committed
110
111
112
113
114
115
    ).unwrap();

    let mut item = 0;
    let mut username = String::new();
    let mut password = String::new();
    let mut failure = false;
116

Jeremy Soller's avatar
Jeremy Soller committed
117
    let mut scaled_image = image.clone();
Jeremy Soller's avatar
Jeremy Soller committed
118
119
120
    let mut mouse_x = 0;
    let mut mouse_y = 0;
    let mut mouse_left = false;
Jeremy Soller's avatar
Jeremy Soller committed
121
122
123
124
125
126
127
128
129
130
131
132
    let mut redraw = true;
    let mut resize = Some((display_width, display_height));
    loop {
        if let Some((w, h)) = resize.take() {
            let (width, height) = find_scale(&image, image_mode, w, h);

            if width == scaled_image.width() && height == scaled_image.height() {
                // Do not resize scaled image
            } else if width == image.width() && height == image.height() {
                scaled_image = image.clone();
            } else {
                scaled_image = image.resize(width, height, orbimage::ResizeType::Lanczos3).unwrap();
Jeremy Soller's avatar
Jeremy Soller committed
133
134
            }

Jeremy Soller's avatar
Jeremy Soller committed
135
            window.set(Color::rgb(0, 0, 0));
Jeremy Soller's avatar
Jeremy Soller committed
136

Jeremy Soller's avatar
Jeremy Soller committed
137
138
139
            let x = (window.width() as i32 - scaled_image.width() as i32)/2;
            let y = (window.height() as i32 - scaled_image.height() as i32)/2;
            scaled_image.draw(&mut window, x, y);
140

Jeremy Soller's avatar
Jeremy Soller committed
141
            let x = (window.width() as i32 - 216)/2;
Jeremy Soller's avatar
Jeremy Soller committed
142
143
144
145
146
            let y = (window.height() as i32 - 164)/2;
            window.rect(x, y, 216, 164, Color::rgba(0, 0, 0, 128));

            font.render("Username:", 16.0).draw(&mut window, x + 8, y + 8, Color::rgb(255, 255, 255));
            font.render("Password:", 16.0).draw(&mut window, x + 8, y + 68, Color::rgb(255, 255, 255));
147

148
            redraw = true;
Jeremy Soller's avatar
Jeremy Soller committed
149
150
151
152
153
        }

        if redraw {
            redraw = false;

154
155
156
157
158
159
160
            let active = if failure {
                Color::rgb(255, 0, 0)
            } else {
                Color::rgb(255, 255, 255)
            };
            let inactive = if failure {
                Color::rgb(128, 0, 0)
Jeremy Soller's avatar
Jeremy Soller committed
161
            } else {
Jeremy Soller's avatar
Jeremy Soller committed
162
                Color::rgb(29, 29, 29)
163
            };
164

Jeremy Soller's avatar
Jeremy Soller committed
165
            let x = (window.width() as i32 - 200)/2;
Jeremy Soller's avatar
Jeremy Soller committed
166
167
168
            let mut y = (window.height() as i32 - 148)/2;

            y += 24;
169

Jeremy Soller's avatar
Jeremy Soller committed
170
171
172
173
174
            {
                window.rect(x, y, 200, 28, if item == 0 { active } else { inactive });
                window.rect(x + 2, y + 2, 196, 24, Color::rgb(40, 40, 40));
                let mut string = username.to_string();
                if item == 0 {
Jeremy Soller's avatar
Jeremy Soller committed
175
                    string.push('|');
Jeremy Soller's avatar
Jeremy Soller committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
                }
                font.render(&string, 16.0).draw(&mut window, x + 6, y + 6, Color::rgb(255, 255, 255));
            }

            y += 60;

            {
                window.rect(x, y, 200, 28, if item == 1 { active } else { inactive });
                window.rect(x + 2, y + 2, 196, 24, Color::rgb(40, 40, 40));
                let mut string = String::new();
                for _c in password.chars() {
                    string.push('•');
                }
                if item == 1 {
Jeremy Soller's avatar
Jeremy Soller committed
190
                    string.push('|');
Jeremy Soller's avatar
Jeremy Soller committed
191
192
193
                }
                font.render(&string, 16.0).draw(&mut window, x + 6, y + 6, Color::rgb(255, 255, 255));
            }
194
195
196

            y += 36;

Jeremy Soller's avatar
Jeremy Soller committed
197
198
199
200
201
            {
                window.rect(x, y, 200, 28, Color::rgb(29, 29, 29));
                window.rect(x + 2, y + 2, 196, 24, Color::rgb(39, 72, 105));
                let text = font.render(&"Login", 16.0);
                text.draw(&mut window, x + (200 - text.width() as i32)/2, y + 6, Color::rgb(255, 255, 255));
Jeremy Soller's avatar
Jeremy Soller committed
202
            }
203

Jeremy Soller's avatar
Jeremy Soller committed
204
205
            window.sync();
        }
206

Jeremy Soller's avatar
Jeremy Soller committed
207
208
209
        for event in window.events() {
            match event.to_option() {
                EventOption::Key(key_event) => if key_event.pressed {
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
                    match key_event.scancode {
                        orbclient::K_BKSP => {
                            if item == 0 {
                                username.pop();
                            } else if item == 1 {
                                password.pop();
                            }

                            redraw = true;
                        },
                        orbclient::K_ENTER => {
                            if item == 0 {
                                item = 1;
                            } else if item == 1 {
                                if let Some(command) = login_command(&username, &password, launcher_cmd, launcher_args) {
                                    return Some(command);
                                } else {
Jeremy Soller's avatar
Jeremy Soller committed
227
                                    item = 0;
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
                                    password.clear();
                                    failure = true
                                }
                            }

                            redraw = true;
                        },
                        orbclient::K_ESC => {
                            item = 0;
                            username.clear();
                            password.clear();
                            failure = false;

                            redraw = true;
                        },
                        orbclient::K_TAB => {
                            if item == 0 {
                                item = 1;
                            } else if item == 1 {
                                item = 0;
                            }

                            redraw = true;
                        },
                        _ => match key_event.character {
                            '\0' => (),
                            _ => {
                                if item == 0 {
                                    username.push(key_event.character);
                                } else if item == 1 {
                                    password.push(key_event.character);
                                }

                                redraw = true;
                            }
                        }
                    }
Jeremy Soller's avatar
Jeremy Soller committed
265
                },
Jeremy Soller's avatar
Jeremy Soller committed
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
                EventOption::Mouse(mouse_event) => {
                    mouse_x = mouse_event.x;
                    mouse_y = mouse_event.y;
                },
                EventOption::Button(button_event) => {
                    if ! button_event.left && mouse_left {
                        let x = (window.width() as i32 - 216)/2;
                        let y = (window.height() as i32 - 164)/2;

                        if mouse_x >= x && mouse_x < x + 216 && mouse_y >= y && mouse_y < y + 164 {
                            if mouse_y < y + 64 {
                                item = 0;
                            } else if mouse_y < y + 128 {
                                item = 1;
                            } else {
                                if let Some(command) = login_command(&username, &password, launcher_cmd, launcher_args) {
                                    return Some(command);
                                } else {
                                    item = 0;
                                    password.clear();
                                    failure = true
                                }
                            }

                            redraw = true;
                        }
                    }
                    mouse_left = button_event.left;
                },
Jeremy Soller's avatar
Jeremy Soller committed
295
296
297
                EventOption::Resize(resize_event) => {
                    resize = Some((resize_event.width, resize_event.height));
                },
298
299
300
301
                EventOption::Screen(screen_event) => {
                    window.set_size(screen_event.width, screen_event.height);
                    resize = Some((screen_event.width, screen_event.height));
                },
Jeremy Soller's avatar
Jeremy Soller committed
302
303
304
305
306
307
308
309
310
311
312
313
314
                EventOption::Quit(_) => return None,
                _ => ()
            }
        }
    }
}

fn main() {
    let mut args = env::args().skip(1);

    let launcher_cmd = args.next().expect("orblogin: no window manager command");
    let launcher_args: Vec<String> = args.collect();

315
316
317
318
319
320
321
322
323
324
325
326
    let font = Font::find(Some("Sans"), None, None).expect("orblogin: no font found");

    let image_mode = BackgroundMode::from_str("zoom");
    let image_path = "/ui/login.png";
    let image = match Image::from_path(image_path) {
        Ok(image) => image,
        Err(err) => {
            println!("orblogin: error loading {}: {}", image_path, err);
            Image::from_color(1, 1, Color::rgb(0x2d, 0x64, 0x8e))
        }
    };

Jeremy Soller's avatar
Jeremy Soller committed
327
    loop {
328
        if let Some(mut command) = login_window(&launcher_cmd, &launcher_args, &font, &image, image_mode) {
Jeremy Soller's avatar
Jeremy Soller committed
329
330
331
332
333
334
335
336
337
            match command.spawn() {
                Ok(mut child) => match child.wait() {
                    Ok(_) => (),
                    Err(err) => {
                        println!("orblogin: failed to wait for '{}': {}", launcher_cmd, err);
                    }
                },
                Err(err) => {
                    println!("orblogin: failed to execute '{}': {}", launcher_cmd, err);
338
339
340
341
342
                }
            }
        }
    }
}