1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
3 // 2011 Free Software Foundation, Inc
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, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 #ifdef HAVE_CONFIG_H
20 #include "gnashconfig.h"
21 #endif
22
23 #include <fltk/Item.h>
24 #include <fltk/Window.h>
25
26 #ifdef HAVE_X11_X_H
27 #include <fltk/x11.h>
28 #endif
29
30 #include <fltk/events.h>
31 #include <fltk/run.h>
32 #include <fltk/Cursor.h>
33 #include <fltk/layout.h>
34 #include <fltk/MenuBar.h>
35 #include <fltk/ItemGroup.h>
36 #include <fltk/file_chooser.h>
37
38 #include "fltksup.h"
39 #include "gui.h"
40 #include "VM.h"
41 #include "RunResources.h"
42
43 #include "Renderer.h"
44
45 using namespace std;
46 using namespace fltk;
47
48
49
50 namespace gnash
51 {
52
53
FltkGui(unsigned long xid,float scale,bool loop,RunResources & r)54 FltkGui::FltkGui(unsigned long xid, float scale, bool loop, RunResources& r)
55 : Window(0, 0),
56 Gui(xid, scale, loop, r),
57 _menu_height(_xid ? 0 : 20)
58 {
59 }
60
~FltkGui()61 FltkGui::~FltkGui()
62 {
63 delete _popup_menu;
64 }
65
66
67 void
renderBuffer()68 FltkGui::renderBuffer()
69 {
70 // FLTK has a nice mechanism where you can set damage() to whatever you want
71 // so in draw() you can check what exactly you want to redraw. But
72 // unfortunately it doesn't seem to remember what bits you turn on. So I'll
73 // just do it the old-fashioned way.
74
75 static bool firstRun = true;
76
77 if (firstRun) {
78 using namespace geometry;
79 Range2d<int> bounds(0, 0, _width, _height);
80 _glue->render(bounds);
81
82 return;
83 }
84
85 if (! _drawbounds_vec.size() ) {
86 return; // XXX what about Cairo?
87 }
88
89 for (unsigned bno=0; bno < _drawbounds_vec.size(); bno++) {
90 geometry::Range2d<int>& bounds = _drawbounds_vec[bno];
91
92 assert ( bounds.isFinite() );
93
94 _glue->render(bounds);
95 }
96 }
97
98 int
handle(int event)99 FltkGui::handle(int event)
100 {
101 switch (event) {
102 case TIMEOUT:
103 advance_movie(this);
104 repeat_timeout(_interval);
105 return true;
106 case PUSH:
107 Window::handle(event);
108 notifyMouseClick(true);
109 return true;
110 case RELEASE:
111 Window::handle(event);
112 notifyMouseClick(false);
113 return true;
114 case MOVE:
115 {
116 if (!_xid && event_y() < static_cast<int>(_menu_height)) {
117 return Window::handle(event);
118 }
119 notifyMouseMove(event_x(), event_y()-_menu_height);
120 return Window::handle(event);
121 }
122 case SHORTCUT:
123 case KEY:
124 handleKey(event_key());
125 return true;
126 default:
127 return Window::handle(event);
128 }
129 }
130
131 void
handleKey(unsigned key)132 FltkGui::handleKey(unsigned key)
133 {
134 // TODO: there are more keys
135 struct {
136 unsigned fltkKey;
137 gnash::key::code gnashKey;
138 } table[] = {
139 { BackSpaceKey, gnash::key::BACKSPACE },
140 { TabKey, gnash::key::TAB },
141 { ClearKey, gnash::key::CLEAR },
142 { ReturnKey, gnash::key::ENTER },
143 { CapsLockKey, gnash::key::CAPSLOCK },
144 { EscapeKey, gnash::key::ESCAPE },
145 { SpaceKey, gnash::key::SPACE },
146 { PageDownKey, gnash::key::PGDN },
147 { PageUpKey, gnash::key::PGUP },
148 { HomeKey, gnash::key::HOME },
149 { EndKey, gnash::key::END },
150 { LeftKey, gnash::key::LEFT },
151 { UpKey, gnash::key::UP },
152 { RightKey, gnash::key::RIGHT },
153 { DownKey, gnash::key::DOWN },
154 { InsertKey, gnash::key::INSERT },
155 { DeleteKey, gnash::key::DELETEKEY },
156 { HelpKey, gnash::key::HELP },
157 { NumLockKey, gnash::key::NUM_LOCK },
158 { SubtractKey, gnash::key::MINUS },
159 { DivideKey, gnash::key::SLASH },
160 { 0, gnash::key::INVALID }
161 #if 0
162 // These appear to be unavailable in fltk
163 { bracketleft, gnash::key::LEFT_BRACKET },
164 { backslash, gnash::key::BACKSLASH },
165 { bracketright, gnash::key::RIGHT_BRACKET },
166 { quotedbl, gnash::key::QUOTE },
167 { VoidSymbol, gnash::key::INVALID }
168 { SemicolonKey, gnash::key::SEMICOLON },
169 { equalKey, gnash::key::EQUALS },
170 #endif
171 };
172
173 int modifier = gnash::key::GNASH_MOD_NONE;
174
175 unsigned long state = event_state();
176
177 if (state & SHIFT) {
178 modifier = modifier | gnash::key::GNASH_MOD_SHIFT;
179 }
180 if (state & CTRL) {
181 modifier = modifier | gnash::key::GNASH_MOD_CONTROL;
182 }
183 if (state & ALT) {
184 modifier = modifier | gnash::key::GNASH_MOD_ALT;
185 }
186
187 for (int i = 0; table[i].fltkKey; i++) {
188 if (key == table[i].fltkKey) {
189 notify_key_event((gnash::key::code)table[i].gnashKey, modifier,
190 true);
191 break;
192 }
193 }
194 }
195
196 bool
run()197 FltkGui::run()
198 {
199 fltk::run();
200
201 return true;
202 }
203
204 bool
init(int,char ***)205 FltkGui::init(int /* argc */, char *** /*argv */)
206 {
207
208 return true;
209 }
210
211 void
setInterval(unsigned int time)212 FltkGui::setInterval(unsigned int time)
213 {
214 _interval = time / 1000.0;
215 add_timeout (_interval);
216 }
217
218 void
create()219 FltkGui::create()
220 {
221 #ifdef HAVE_X11_X_H
222 if (_xid) {
223 // Make FLTK render into an X window indicated by the XID.
224 CreatedWindow::set_xid(this, _xid);
225 return;
226 }
227 #endif
228 Window::create();
229 }
230
231 bool
createWindow(const char * title,int width,int height,int xPosition,int yPosition)232 FltkGui::createWindow(const char* title, int width, int height,
233 int xPosition, int yPosition)
234 {
235 resize(width, _menu_height + height);
236
237
238 label(title);
239 begin();
240
241 if (!_xid) {
242 MenuBar* menubar = new MenuBar(0, 0, width, _menu_height);
243 menubar->begin();
244 addMenuItems();
245 menubar->end();
246 }
247 #ifdef RENDERER_AGG
248 _glue = new FltkAggGlue(0, _menu_height, width, height);
249 #elif defined(RENDERER_CAIRO)
250 #error FLTK/Cairo is currently broken. Please try again soon...
251 FltkCairoGlue _glue;
252 #elif defined(RENDERER_OPENGL)
253 #error FLTK/OpenGL is currently broken. Please try again soon...
254 FltkCairoGlue _glue;
255 #endif
256 createMenu();
257 end();
258
259 _renderer.reset(_glue->createRenderHandler());
260 if (!_renderer.get()) return false;
261 _runResources.setRenderer(_renderer);
262
263 _glue->initBuffer(width, height);
264
265 // The minimum size of the window is 1x1 pixels.
266 size_range (1, 1);
267
268 show();
269
270 return true;
271 }
272
273
fltk_menu_open_file(Widget *,void *)274 static void fltk_menu_open_file(Widget*, void*)
275 {
276 const char *newfile = fltk::file_chooser("Open File", "*.swf", NULL);
277 if (!newfile) {
278 return;
279 }
280
281 // menu_open_file()..
282 }
283
fltk_menu_save_file_as(Widget *,void *)284 static void fltk_menu_save_file_as(Widget*, void*)
285 {
286 const char* savefile = file_chooser("Save File as", NULL, NULL);
287 if (!savefile) {
288 return;
289 }
290
291 // menu_save_file();
292 }
293
fltk_menu_fullscreen(Widget *,void * ptr)294 static void fltk_menu_fullscreen(Widget*, void* ptr)
295 {
296 // GNASH_REPORT_FUNCTION;
297
298 static bool fullscreen = false;
299 static Rectangle oldBounds;
300
301 fullscreen = !fullscreen;
302
303 FltkGui* gui = static_cast<FltkGui*>(ptr);
304 if (fullscreen) {
305 oldBounds.set(gui->x(), gui->y(), gui->w(), gui->h());
306 gui->fullscreen();
307 } else {
308 gui->fullscreen_off(oldBounds.x(), oldBounds.y(), oldBounds.w(), oldBounds.h());
309 }
310 }
311
312
313 static void
fltk_menu_quit(Widget *,void * ptr)314 fltk_menu_quit(Widget*, void* ptr)
315 {
316 FltkGui* gui = static_cast<FltkGui*>(ptr);
317 gui->quit();
318 }
319
320 static void
fltk_menu_play(Widget *,void * ptr)321 fltk_menu_play(Widget*, void* ptr)
322 {
323 FltkGui* gui = static_cast<FltkGui*>(ptr);
324 gui->play();
325 }
326
327 static void
fltk_menu_pause(Widget *,void * ptr)328 fltk_menu_pause(Widget*, void* ptr)
329 {
330 FltkGui* gui = static_cast<FltkGui*>(ptr);
331 gui->pause();
332 }
333
334 static void
fltk_menu_stop(Widget *,void * ptr)335 fltk_menu_stop(Widget*, void* ptr)
336 {
337 FltkGui* gui = static_cast<FltkGui*>(ptr);
338 gui->stop();
339 }
340
341 static void
fltk_menu_restart(Widget *,void * ptr)342 fltk_menu_restart(Widget*, void* ptr)
343 {
344 FltkGui* gui = static_cast<FltkGui*>(ptr);
345 gui->restart();
346 }
347
348 static void
fltk_menu_toggle_sound(Widget *,void * ptr)349 fltk_menu_toggle_sound(Widget*, void* ptr)
350 {
351 FltkGui* gui = static_cast<FltkGui*>(ptr);
352 gui->toggleSound();
353 }
354
355 void
addMenuItems()356 FltkGui::addMenuItems()
357 {
358 ItemGroup* file = new ItemGroup("File");
359
360
361 file->begin();
362 new Item("Open", 0, fltk_menu_open_file);
363 new Item("Save as", 0, fltk_menu_save_file_as);
364 new Item("Quit", 0, fltk_menu_quit, this);
365 file->end();
366
367 ItemGroup* edit = new ItemGroup("Edit");
368 edit->begin();
369 new Item("Preferences");
370 edit->end();
371
372 ItemGroup* view = new ItemGroup("View");
373 view->begin();
374 new Item("Double size");
375 new Item("Fullscreen", 0, fltk_menu_fullscreen, this);
376 view->end();
377
378 ItemGroup* movie_ctrl = new ItemGroup("Movie control");
379 movie_ctrl->begin();
380 new Item("Play Movie", 0, fltk_menu_play, this);
381 new Item("Pause Movie", 0, fltk_menu_pause, this);
382 new Item("Stop Movie", 0, fltk_menu_stop, this);
383 new Item("Restart Movie", 0, fltk_menu_restart, this);
384 new Item("Toggle Sound", 0, fltk_menu_toggle_sound, this);
385 movie_ctrl->end();
386
387 ItemGroup* help = new ItemGroup("Help");
388 help->begin();
389 new Item("About");
390 help->end();
391 }
392
393
394
395 bool
createMenu()396 FltkGui::createMenu()
397 {
398 _popup_menu = new PopupMenu(0, 0, w(), h());
399 _popup_menu->type(PopupMenu::POPUP3);
400
401 _popup_menu->begin();
402
403 addMenuItems();
404
405 _popup_menu->end();
406
407 return true;
408 }
409
410 void
layout()411 FltkGui::layout()
412 {
413 if (!VM::isInitialized()) {
414 // No movie yet; don't bother resizing anything.
415 return;
416 }
417
418 // Let FLTK update the window borders, etc.
419 Window::layout();
420
421 if ((layout_damage() & LAYOUT_WH)) {
422 _glue->resize(w(), h() - _menu_height);
423 resize_view(w(), h() - _menu_height);
424 }
425 }
426
427 void
setCursor(gnash_cursor_type newcursor)428 FltkGui::setCursor(gnash_cursor_type newcursor)
429 {
430 fltk::Cursor* cursortype;
431
432 switch(newcursor) {
433 case gnash::CURSOR_HAND:
434 cursortype = fltk::CURSOR_HAND;
435 break;
436 case gnash::CURSOR_INPUT:
437 cursortype = fltk::CURSOR_INSERT;
438 break;
439 default:
440 cursortype = fltk::CURSOR_DEFAULT;
441 }
442
443 cursor(cursortype);
444 }
445
446 void
setInvalidatedRegions(const InvalidatedRanges & ranges)447 FltkGui::setInvalidatedRegions(const InvalidatedRanges& ranges)
448 {
449 // forward to renderer
450 //
451 // Why? Why have the region been invalidated ??
452 // Was the renderer offscreen buffer also invalidated
453 // (need to rerender)?
454 // Was only the 'onscreen' buffer be invalidated (no need to rerender,
455 // just to blit) ??
456 //
457 // To be safe just assume this 'invalidated' region is actually
458 // the offscreen buffer, for safety, but we need to clarify this.
459 //
460 _renderer->set_invalidated_regions(ranges);
461
462 _drawbounds_vec.clear();
463
464 for (size_t rno=0; rno<ranges.size(); rno++) {
465
466 geometry::Range2d<int> bounds = Intersection(
467 _renderer->world_to_pixel(ranges.getRange(rno)),
468 _validbounds);
469
470 // it may happen that a particular range is out of the screen, which
471 // will lead to bounds==null.
472 if (bounds.isNull()) continue;
473
474 assert(bounds.isFinite());
475
476 _drawbounds_vec.push_back(bounds);
477
478 }
479 }
480
481 // end of namespace
482 }
483
484