2 * \brief DOpE VScreen widget module
4 * \author Norman Feske <nf2@inf.tu-dresden.de>
8 * Copyright (C) 2002-2004 Norman Feske <nf2@os.inf.tu-dresden.de>
9 * Technische Universitaet Dresden, Operating Systems Research Group
11 * This file is part of the DOpE package, which is distributed under
12 * the terms of the GNU General Public Licence 2. Please see the
13 * COPYING file for details.
17 #define WIDGET struct vscreen
22 #include "scheduler.h"
23 #include "widget_data.h"
24 #include "widget_help.h"
28 #include "vscr_server.h"
32 #include "userstate.h"
33 #include "messenger.h"
37 #include <l4/re/env.h>
41 #define VSCR_MOUSEMODE_FREE 0
42 #define VSCR_MOUSEMODE_GRAB 1
43 #define VSCR_MOUSEMODE_GRABBED 2
45 #define VSCR_UPDATE_MOUSE_X 0x1
46 #define VSCR_UPDATE_MOUSE_Y 0x2
48 static struct scheduler_services *sched;
49 static struct widman_services *widman;
50 static struct script_services *script;
51 static struct redraw_services *redraw;
52 static struct appman_services *appman;
53 static struct gfx_services *gfx;
54 static struct messenger_services *msg;
55 static struct userstate_services *userstate;
56 static struct thread_services *thread;
57 static struct vscr_server_services *vscr_server;
58 static struct fontman_services *font;
62 char *server_ident; /* associated vscreen server identifier */
63 THREAD *server_thread;
65 s8 bpp; /* bits per pixel */
66 s32 xres, yres; /* virtual screen dimensions */
67 s16 fps; /* frames per second */
68 char smb_ident[64]; /* shared memory block identifier */
69 void *pixels; /* pointer to page aligned pixel buffer */
70 GFX_CONTAINER *image; /* image representation (for drawing) */
71 s16 grabmouse; /* mouse grab mode flag 0=free 1=grab 2=grabbed */
72 VSCREEN *share_next; /* next vscreen widget with shared buffer */
73 s32 vw, vh; /* view size */
74 s32 curr_vx, curr_vy; /* current view position */
75 s32 next_vx, next_vy; /* next view position */
78 static int msg_cnt, msg_fid; /* fade cnt and font of on-screen msg */
79 static int msg_x, msg_y; /* position of onscreen message */
80 static int msg_w, msg_h; /* size of onscreen message area */
81 static char *msg_txt; /* text of the onscreen message */
83 static int vs_mx, vs_my; /* mouse position of grabbed mouse */
85 int init_vscreen(struct dope_services *d);
88 /*************************
89 *** UTILITY FUNCTIONS ***
90 *************************/
92 /*** EXCLUDE VSCREEN WIDGET FROM RING LIST OF VSCREENS WITH A SHARED BUFFER ***/
93 static void vscr_share_exclude(VSCREEN *vs) {
94 VSCREEN *prev = vs->vd->share_next;
95 while ((prev) && (prev->vd->share_next!=vs)) prev = prev->vd->share_next;
96 if (prev) prev->vd->share_next = vs->vd->share_next;
97 vs->vd->share_next = NULL;
100 gfx->dec_ref(vs->vd->image);
101 vs->vd->image = NULL;
106 /*** INCLUDE NEW VSCREEN IN RING LIST OF VSCREENS WITH A SHARED BUFFER ***
108 * \param vs member of the new desired ring list
109 * \param new new vscreen widget to join the list
111 static void vscr_share_join(VSCREEN *vs, VSCREEN *new) {
112 if (new->vd->share_next) vscr_share_exclude(new);
113 new->vd->share_next = vs->vd->share_next;
114 vs->vd->share_next = new;
115 if (!new->vd->share_next) new->vd->share_next = vs;
119 /******************************
120 *** GENERAL WIDGET METHODS ***
121 ******************************/
123 /*** DRAW VSCREEN WIDGET ***/
124 static int vscr_draw(VSCREEN *vs, struct gfx_ds *ds, long x, long y, WIDGET *origin) {
128 if (origin == vs) return 1;
129 if (origin) return 0;
131 gfx->push_clipping(ds, x, y, vs->wd->w, vs->wd->h);
133 float xratio = (float)vs->wd->w / (float)vs->vd->vw;
134 float yratio = (float)vs->wd->h / (float)vs->vd->vh;
135 int sx = xratio * vs->vd->curr_vx;
136 int sy = yratio * vs->vd->curr_vy;
137 int sw = xratio * vs->vd->xres;
138 int sh = yratio * vs->vd->yres;
140 gfx->draw_img(ds, x - sx, y - sy, sw, sh, vs->vd->image, 255);
142 if ((vs->vd->grabmouse == VSCR_MOUSEMODE_GRABBED) && (msg_cnt)) {
144 gfx->draw_string(ds, x+msg_x-1, y+msg_y, GFX_RGB(0, 0, 0), 0, msg_fid, msg_txt);
145 gfx->draw_string(ds, x+msg_x+1, y+msg_y, GFX_RGB(0, 0, 0), 0, msg_fid, msg_txt);
146 gfx->draw_string(ds, x+msg_x, y+msg_y-1, GFX_RGB(0, 0, 0), 0, msg_fid, msg_txt);
147 gfx->draw_string(ds, x+msg_x, y+msg_y+1, GFX_RGB(0, 0, 0), 0, msg_fid, msg_txt);
148 gfx->draw_string(ds, x+msg_x, y+msg_y, GFX_RGB(v, v, v), 0, msg_fid, msg_txt);
150 gfx->pop_clipping(ds);
156 static void (*orig_update) (VSCREEN *vs);
158 /*** UPDATE WIDGET AFTER CHANGES OF ITS ATTRIBUTES ***/
159 static void vscr_update(VSCREEN *vs)
163 if (vs->vd->update_flags & (VSCR_UPDATE_MOUSE_X | VSCR_UPDATE_MOUSE_Y))
165 if (!(vs->vd->update_flags & VSCR_UPDATE_MOUSE_X))
166 vs_mx = userstate->get_mx();
168 if (!(vs->vd->update_flags & VSCR_UPDATE_MOUSE_Y))
169 vs_my = userstate->get_my();
171 event.time = l4_kip_clock(l4re_kip());
172 event.type = L4RE_EV_ABS;
173 event.code = L4RE_ABS_X;
174 event.value = vs_mx - vs->gen->get_abs_x(vs);
175 vs->gen->handle_event(vs, &event, NULL);
177 event.type = L4RE_EV_ABS;
178 event.code = L4RE_ABS_Y;
179 event.value = vs_my - vs->gen->get_abs_y(vs);
180 vs->gen->handle_event(vs, &event, NULL);
182 /* warp mouse to new position and handle the synthetic event */
183 userstate->set_pos(vs_mx, vs_my);
186 vs->vd->update_flags = 0;
190 /*** TIMER TICK CALLBACK FOR GRAB USERSTATE ***
192 * This callback is used to fade out the mouse-release-message.
193 * The variable msg_cnt is set to 255 when the mouse is grabbed and faded
194 * down to zero. For each step the part of the widget which displays the
195 * message is redrawn.
198 static void grab_tick_callback(WIDGET *w, int dx, int dy) {
202 if (w->vd->fps == 0) redraw->draw_widgetarea(w, msg_x-1, msg_y-1, msg_x+msg_w+1, msg_y+msg_h+1);
203 } else if (msg_cnt > 0) {
205 if (w->vd->fps == 0) redraw->draw_widgetarea(w, msg_x-1, msg_y-1, msg_x+msg_w+1, msg_y+msg_h+1);
213 /*** HANDLE EVENTS ***
215 * We have to take care about the mouse grab mode of the VScreen widget.
216 * In grab-mode the mouse is grabbed by any press event onto the widget.
217 * When grabbed, the mouse can be discharged by pressing [pause]. All
218 * other events go through the normal way.
220 static void (*orig_handle_event)(WIDGET *w, EVENT *, WIDGET *from);
221 static void vscr_handle_event(VSCREEN *vs, EVENT *e, WIDGET *from) {
225 if (e->type == EVENT_PRESS) {
226 /* transition from grabbed to grab */
227 if (vs->vd->grabmouse == VSCR_MOUSEMODE_GRABBED) {
228 if (e->code == L4RE_KEY_PAUSE) {
229 vs->vd->grabmouse = VSCR_MOUSEMODE_GRAB;
230 m = vs->gen->get_bind_msg(vs, "discharge");
231 app_id = vs->gen->get_app_id(vs);
232 if (m) msg->send_action_event(app_id, "discharge", m);
234 redraw->draw_widgetarea(vs, msg_x, msg_y, msg_x + msg_w, msg_y + msg_h);
238 /* transition to grabbed mouse */
239 } else if (vs->vd->grabmouse == VSCR_MOUSEMODE_GRAB) {
242 /* top associated window */
243 w = (WINDOW *)vs->gen->get_window(vs);
244 if (w) w->win->top(w);
246 vs->vd->grabmouse = VSCR_MOUSEMODE_GRABBED;
247 m = vs->gen->get_bind_msg(vs, "catch");
248 app_id = vs->gen->get_app_id(vs);
249 if (m) msg->send_action_event(app_id, "catch", m);
250 msg_x = (vs->wd->w - msg_w)/2;
251 msg_y = (vs->wd->h - msg_h)/2;
252 userstate->grab(vs, grab_tick_callback);
258 /* scale values of motion event */
259 if (e->type == L4RE_EV_ABS)
262 if (e->code == L4RE_ABS_X)
263 ratio = (float)vs->vd->xres / (float)vs->wd->w;
264 else if (e->code == L4RE_ABS_Y)
265 ratio = (float)vs->vd->yres / (float)vs->wd->h;
267 e->value = (float)e->value * ratio;
269 orig_handle_event(vs, e, from);
273 /*** RELEASE VSCREEN DATA ***
275 * Eventually, we have to exclude the vscreen from
276 * the ring list of vscreens which share one buffer.
278 static void vscr_free_data(VSCREEN *vs) {
280 /* shutdown vscreen server thread */
281 if (vs->vd->server_thread)
282 thread->kill_thread(vs->vd->server_thread);
284 vs->vd->server_thread = NULL;
286 /* free the mouse if it is currently grabbed inside the vscreen widget */
287 if ((userstate->get() == USERSTATE_GRAB) && (userstate->get_selected() == vs))
290 if (vs->wd->ref_cnt == 0) vscr_share_exclude(vs);
294 /*** RETURN WIDGET TYPE IDENTIFIER ***/
295 static char *vscr_get_type(VSCREEN *vs) {
301 /********************************
302 *** VSCREEN SPECIFIC METHODS ***
303 ********************************/
305 /*** REGISTER VSCREEN WIDGET SERVER ***/
306 static void vscr_reg_server(VSCREEN *vs, char *new_server_ident) {
307 if (!new_server_ident) return;
308 INFO(printf("Vscreen(reg_server): register Vscreen server with ident=%s\n", new_server_ident));
309 vs->vd->server_ident = new_server_ident;
313 /*** RETURN VSCREEN WIDGET SERVER ***/
314 static char *vscr_get_server(VSCREEN *vs) {
316 /* allocate thread id for server thread */
317 vs->vd->server_thread = thread->alloc_thread();
319 /* start widget server */
320 if (vscr_server && vs->vd->server_thread)
321 vscr_server->start(vs->vd->server_thread, vs);
323 INFO(printf("VScreen(get_server): server_ident = %s\n", vs->vd->server_ident));
324 return vs->vd->server_ident;
328 /*** SET UPDATE RATE OF THE VSCREEN WIDGET ***/
329 static void vscr_set_framerate(VSCREEN *vs, s32 framerate) {
331 if (framerate == 0) {
334 sched->add(vs, 1000/framerate);
335 sched->set_sync_mutex(vs, vs->vd->sync_mutex);
336 vs->vd->fps = framerate;
341 /*** REQUEST CURRENT UPDATE RATE ***/
342 static s32 vscr_get_framerate(VSCREEN *vs) {
347 /*** SET VSCREEN MOUSE X POSITION ***/
348 static void vscr_set_mx(VSCREEN *vs, s32 new_mx) {
349 if (vs->vd->grabmouse != VSCR_MOUSEMODE_GRABBED) return;
350 vs_mx = new_mx + vs->gen->get_abs_x(vs);
351 vs->vd->update_flags |= VSCR_UPDATE_MOUSE_X;
355 /*** REQUEST VSCREEN MOUSE X POSITION ***/
356 static s32 vscr_get_mx(VSCREEN *vs) {
357 if (vs->vd->grabmouse != VSCR_MOUSEMODE_GRABBED) return 0;
358 return vs_mx - vs->gen->get_abs_x(vs);
362 /*** SET VSCREEN MOUSE Y POSITION ***/
363 static void vscr_set_my(VSCREEN *vs, s32 new_my) {
364 if (vs->vd->grabmouse != VSCR_MOUSEMODE_GRABBED) return;
365 vs_my = new_my + vs->gen->get_abs_y(vs);
366 vs->vd->update_flags |= VSCR_UPDATE_MOUSE_Y;
370 /*** SET WIDTH OF A VSCREEN TO A FIXED VALUE ***/
371 static void vscr_set_fixw(VSCREEN *vs, char *new_fixw) {
372 if (!new_fixw) return;
373 if (dope_streq("none", new_fixw, 4)) {
375 vs->wd->max_w = 99999;
377 vs->wd->min_w = vs->wd->max_w = atol(new_fixw);
379 vs->wd->update |= WID_UPDATE_MINMAX;
383 /*** SET HEIGHT OF A VSCREEN TO A FIXED VALUE ***/
384 static void vscr_set_fixh(VSCREEN *vs, char *new_fixh) {
385 if (!new_fixh) return;
386 if (dope_streq("none", new_fixh, 4)) {
388 vs->wd->max_h = 99999;
390 vs->wd->min_h = vs->wd->max_h = atol(new_fixh);
392 vs->wd->update |= WID_UPDATE_MINMAX;
396 /*** REQUEST VSCREEN MOUSE Y POSITION ***/
397 static s32 vscr_get_my(VSCREEN *vs) {
398 if (vs->vd->grabmouse != VSCR_MOUSEMODE_GRABBED) return 0;
399 return vs_my - vs->gen->get_abs_y(vs);
403 /*** SET MOUSE GRAB MODE OF THE VSCREEN WIDGET ***/
404 static void vscr_set_grabmouse(VSCREEN *vs, s32 grab_flag) {
405 vs->vd->grabmouse = grab_flag ? VSCR_MOUSEMODE_GRAB : VSCR_MOUSEMODE_FREE;
409 /*** REQUEST MOUSE GRAB MODE ***/
410 static s32 vscr_get_grabmouse(VSCREEN *vs) {
411 if (vs->vd->grabmouse == VSCR_MOUSEMODE_FREE) return 0;
416 /*** TEST IF A GIVEN GRAPHICS MODE IS VALID ***/
417 static s32 vscr_probe_mode(VSCREEN *vs, s32 width, s32 height, char *mode) {
419 if ((!dope_streq(mode, "RGB16", 6))
420 && (!dope_streq(mode, "YUV420", 7))
421 && (!dope_streq(mode, "RGBA32", 7))) return 0;
422 if (width*height <= 0) return 0;
427 /*** SET GRAPHICS MODE ***/
428 static s32 vscr_set_mode(VSCREEN *vs, s32 width, s32 height, char *mode) {
431 if (!vscr_probe_mode(vs, width, height, mode)) return 0;
433 /* destroy old image buffer and reset values */
434 switch (vs->vd->bpp) {
437 if (vs->vd->image) gfx->dec_ref(vs->vd->image);
443 vs->vd->pixels = NULL;
444 vs->vd->image = NULL;
445 vs->vd->vw = vs->vd->vh = 0;
446 vs->vd->curr_vx = vs->vd->next_vx = 0;
447 vs->vd->curr_vy = vs->vd->next_vy = 0;
449 /* create new frame buffer image */
450 if (dope_streq("RGB16", mode, 6)) type = GFX_IMG_TYPE_RGB16;
451 if (dope_streq("RGBA32", mode, 7)) type = GFX_IMG_TYPE_RGBA32;
452 if (dope_streq("YUV420", mode, 7)) type = GFX_IMG_TYPE_YUV420;
455 ERROR(printf("VScreen(set_mode): mode %s not supported!\n", mode);)
459 if ((vs->vd->image = gfx->alloc_img(width, height, type))) {
460 vs->vd->xres = width;
461 vs->vd->yres = height;
464 vs->vd->pixels = gfx->map(vs->vd->image);
465 gfx->get_ident(vs->vd->image, &vs->vd->smb_ident[0]);
467 ERROR(printf("VScreen(set_mode): out of memory!\n");)
471 if (type == GFX_IMG_TYPE_RGBA32)
472 vs->wd->flags &= ~WID_FLAGS_CONCEALING;
474 vs->wd->flags |= WID_FLAGS_CONCEALING;
481 /*** WAIT FOR THE NEXT SYNC ***/
482 static void vscr_waitsync(VSCREEN *vs) {
483 thread->mutex_down(vs->vd->sync_mutex);
487 /*** UPDATE A SPECIFIED AREA OF THE WIDGET ***/
488 static void vscr_refresh(VSCREEN *vs, s32 x, s32 y, s32 w, s32 h) {
490 int sx1, sy1, sx2, sy2;
494 if (vs->vd->share_next) {
495 while ((vs = vs->vd->share_next) != last) cnt++;
498 if (w == -1) w = vs->vd->xres;
499 if (h == -1) h = vs->vd->yres;
500 if ((w<1) || (h<1)) return;
502 /* refresh all vscreens which share the same pixel buffer */
504 mx = (float)vs->wd->w / (float)vs->vd->xres;
505 my = (float)vs->wd->h / (float)vs->vd->yres;
508 sx2 = (int)((x + w)*mx);
509 sy2 = (int)((y + h)*my);
510 redraw->draw_widgetarea(vs, sx1, sy1, sx2, sy2);
511 vs = vs->vd->share_next;
516 /*** MAP VSCREEN BUFFER TO ANOTHER THREAD'S ADDRESS SPACE ***/
517 static char *vscr_map(VSCREEN *vs, char *dst_thread_ident) {
518 (void)dst_thread_ident;
521 THREAD *dst_th = (THREAD *)(void *)dst_th_buf;
523 if (!vs->vd->image) return "Error: VScreen mode not initialized.";
525 /* if no valid thread identifier was suppied we map to the app's thread */
526 /*if (thread->ident2thread(dst_thread_ident, dst_th))*/ {
527 app_id = vs->gen->get_app_id(vs);
528 dst_th = appman->get_app_thread(app_id);
530 gfx->share(vs->vd->image, dst_th);
531 INFO(printf("VScreen(map): return vs->vd->smb_ident = %s\n", &vs->vd->smb_ident[0]));
532 return &vs->vd->smb_ident[0];
536 /*** SHARE IMAGE BUFFER WITH OTHER SPECIFIED VSCREEN ***/
537 static void vscr_share(VSCREEN *vs, VSCREEN *from) {
539 GFX_CONTAINER *new_image;
541 /* exclude widget from its previous ring list of buffer-shared vscreen */
542 if (vs->vd->share_next) vscr_share_exclude(vs);
544 if (!from || !(new_image = from->vscr->get_image(from))) return;
547 * Increment reference counter of new image before decrementing
548 * the reference counter of the old one. If both images are the
549 * same, we do not want to risk a reference counter of zero.
551 gfx->inc_ref(new_image);
553 /* replace old image with new one */
554 if (vs->vd->image) gfx->dec_ref(vs->vd->image);
555 vs->vd->image = new_image;
557 vs->vd->vw = vs->vd->xres = gfx->get_width(vs->vd->image);
558 vs->vd->vh = vs->vd->yres = gfx->get_height(vs->vd->image);
559 img_type = gfx->get_type(vs->vd->image);
561 case GFX_IMG_TYPE_RGB16:
564 case GFX_IMG_TYPE_RGBA32:
567 case GFX_IMG_TYPE_YUV420:
571 vs->vd->pixels = gfx->map(vs->vd->image);
573 vs->gen->set_w(vs, vs->vd->xres);
574 vs->gen->set_h(vs, vs->vd->yres);
575 vs->gen->updatepos(vs);
577 /* join the new ring list of buffer sharing vscreens */
578 vscr_share_join(from, vs);
582 static GFX_CONTAINER *vscr_get_image(VSCREEN *vs) {
583 return vs->vd->image;
587 static struct widget_methods gen_methods;
588 static struct vscreen_methods vscreen_methods = {
596 /*************************
597 *** SERVICE FUNCTIONS ***
598 *************************/
600 static VSCREEN *create(void) {
602 VSCREEN *new = ALLOC_WIDGET(struct vscreen);
603 SET_WIDGET_DEFAULTS(new, struct vscreen, &vscreen_methods);
605 /* set widget type specific data */
606 new->vd->sync_mutex = thread->create_mutex(1); /* locked */
607 new->wd->flags |= WID_FLAGS_CONCEALING | WID_FLAGS_EDITABLE;
612 /****************************************
613 *** SERVICE STRUCTURE OF THIS MODULE ***
614 ****************************************/
616 static struct vscreen_services services = {
621 /**************************
622 *** MODULE ENTRY POINT ***
623 **************************/
625 static void build_script_lang(void) {
628 widtype = script->reg_widget_type("VScreen", (void *(*)(void))create);
630 script->reg_widget_method(widtype, "long probemode(long width, long height, string mode)", vscr_probe_mode);
631 script->reg_widget_method(widtype, "long setmode(long width, long height, string mode)", vscr_set_mode);
632 script->reg_widget_method(widtype, "string getserver()", vscr_get_server);
633 script->reg_widget_method(widtype, "string map(string thread=\"caller\")", vscr_map);
634 script->reg_widget_method(widtype, "void refresh(long x=0, long y=0, long w=-1, long h=-1)", vscr_refresh);
635 script->reg_widget_method(widtype, "void share(Widget from)", vscr_share);
637 script->reg_widget_attrib(widtype, "long framerate", vscr_get_framerate, vscr_set_framerate, gen_methods.update);
638 script->reg_widget_attrib(widtype, "string fixw", NULL, vscr_set_fixw, gen_methods.update);
639 script->reg_widget_attrib(widtype, "string fixh", NULL, vscr_set_fixh, gen_methods.update);
640 script->reg_widget_attrib(widtype, "long mousex", vscr_get_mx, vscr_set_mx, gen_methods.update);
641 script->reg_widget_attrib(widtype, "long mousey", vscr_get_my, vscr_set_my, gen_methods.update);
642 script->reg_widget_attrib(widtype, "boolean grabmouse", vscr_get_grabmouse, vscr_set_grabmouse, gen_methods.update);
643 widman->build_script_lang(widtype, &gen_methods);
647 int init_vscreen(struct dope_services *d) {
649 gfx = d->get_module("Gfx 1.0");
650 script = d->get_module("Script 1.0");
651 widman = d->get_module("WidgetManager 1.0");
652 redraw = d->get_module("RedrawManager 1.0");
653 appman = d->get_module("ApplicationManager 1.0");
654 thread = d->get_module("Thread 1.0");
655 sched = d->get_module("Scheduler 1.0");
656 vscr_server = d->get_module("VScreenServer 1.0");
657 msg = d->get_module("Messenger 1.0");
658 userstate = d->get_module("UserState 1.0");
659 font = d->get_module("FontManager 1.0");
661 /* define general widget functions */
662 widman->default_widget_methods(&gen_methods);
664 orig_update = gen_methods.update;
665 orig_handle_event = gen_methods.handle_event;
667 gen_methods.get_type = vscr_get_type;
668 gen_methods.draw = vscr_draw;
669 gen_methods.update = vscr_update;
670 gen_methods.free_data = vscr_free_data;
671 gen_methods.handle_event = vscr_handle_event;
676 msg_txt = "press [pause] to release mouse";
677 msg_w = font->calc_str_width(msg_fid, msg_txt);
678 msg_h = font->calc_str_height(msg_fid, msg_txt);
680 d->register_module("VScreen 1.0", &services);