2 * GUI for console video.
3 * The routines here are in charge of loading the keypad and handling events.
8 * GUI layout, structure and management
10 For the GUI we use SDL to create a large surface (gui->screen) with 4 areas:
11 remote video on the left, local video on the right, keypad with all controls
12 and text windows in the center, and source device thumbnails on the top.
13 The top row is not displayed if no devices are specified in the config file.
15 ________________________________________________________________
16 | ______ ______ ______ ______ ______ ______ ______ |
17 | | tn.1 | | tn.2 | | tn.3 | | tn.4 | | tn.5 | | tn.6 | | tn.7 | |
18 | |______| |______| |______| |______| |______| |______| |______| |
19 | ______ ______ ______ ______ ______ ______ ______ |
20 | |______| |______| |______| |______| |______| |______| |______| |
21 | _________________ __________________ _________________ |
25 | | remote video | | | | local video | |
26 | | | | | | ______ | |
27 | | | | keypad | | | PIP || |
28 | | | | | | |______|| |
29 | |_________________| | | |_________________| |
32 | |__________________| |
33 |________________________________________________________________|
36 The central section is built using an image (jpg, png, maybe gif too)
37 for the skin, and other GUI elements. Comments embedded in the image
38 indicate to what function each area is mapped to.
39 Another image (png with transparency) is used for the font.
41 Mouse and keyboard events are detected on the whole surface, and
42 handled differently according to their location:
43 - center/right click on the local/remote window are used to resize
44 the corresponding window;
45 - clicks on the thumbnail start/stop sources and select them as
46 primary or secondary video sources;
47 - drag on the local video window are used to move the captured
48 area (in the case of X11 grabber) or the picture-in-picture position;
49 - keystrokes on the keypad are mapped to the corresponding key;
50 keystrokes are used as keypad functions, or as text input
51 if we are in text-input mode.
52 - drag on some keypad areas (sliders etc.) are mapped to the
53 corresponding functions (mute/unmute audio and video,
54 enable/disable Picture-in-Picture, freeze the incoming video,
55 dial numbers, pick up or hang up a call, ...)
57 Configuration options control the appeareance of the gui:
59 keypad = /tmp/kpad2.jpg ; the skin
60 keypad_font = /tmp/font.png ; the font to use for output
62 For future implementation, intresting features can be the following:
63 - save of the whole SDL window as a picture
64 - audio output device switching
66 The audio switching feature should allow changing the device
67 or switching to a recorded message for audio sent to remote party.
68 The selection of the device should happen clicking on a marker in the layout.
69 For this reason above the thumbnails row in the layout we would like a new row,
70 the elements composing the row could be message boards, reporting the name of the
71 device or the path of the message to be played.
73 For video input freeze and entire window capture, we define 2 new key types,
74 those should be activated pressing the buttons on the keypad, associated with
75 new regions inside the keypad pictureas comments
82 #include "console_video.h"
83 #include "asterisk/lock.h"
84 #include "asterisk/frame.h"
85 #include "asterisk/utils.h" /* ast_calloc and ast_realloc */
86 #include <math.h> /* sqrt */
88 /* We use a maximum of 12 'windows' in the GUI */
89 enum { WIN_LOCAL
, WIN_REMOTE
, WIN_KEYPAD
, WIN_SRC1
,
90 WIN_SRC2
, WIN_SRC3
, WIN_SRC4
, WIN_SRC5
,
91 WIN_SRC6
, WIN_SRC7
, WIN_SRC8
, WIN_SRC9
, WIN_MAX
};
93 #ifndef HAVE_SDL /* stubs if we don't have any sdl */
94 static void show_frame(struct video_desc
*env
, int out
) {}
95 static void sdl_setup(struct video_desc
*env
) {}
96 static struct gui_info
*cleanup_sdl(struct gui_info
* g
, int n
) { return NULL
; }
97 static void eventhandler(struct video_desc
*env
, const char *caption
) {}
98 static int keypad_cfg_read(struct gui_info
*gui
, const char *val
) { return 0; }
100 #else /* HAVE_SDL, the real rendering code */
103 #include <SDL/SDL_syswm.h>
104 #ifdef HAVE_SDL_IMAGE
105 #include <SDL/SDL_image.h> /* for loading images */
109 /* Need to hook into X for SDL_WINDOWID handling */
110 #include <X11/Xlib.h>
113 #define BORDER 5 /* border around our windows */
114 #define SRC_MSG_BD_H 20 /* height of the message board below those windows */
116 enum kp_type
{ KP_NONE
, KP_RECT
, KP_CIRCLE
};
117 struct keypad_entry
{
118 int c
; /* corresponding character */
119 int x0
, y0
, x1
, y1
, h
; /* arguments */
123 /* our representation of a displayed window. SDL can only do one main
124 * window so we map everything within that one
126 struct display_window
{
128 SDL_Rect rect
; /* location of the window */
131 /* each thumbnail message board has a rectangle associated for the geometry,
132 * and a board structure, we include these two elements in a singole structure */
134 SDL_Rect rect
; /* the rect for geometry and background */
135 struct board
*board
; /* the board */
139 enum kb_output kb_output
; /* where the keyboard output goes */
140 struct drag_info drag
; /* info on the window are we dragging */
141 /* support for display. */
142 SDL_Surface
*screen
; /* the main window */
144 int outfd
; /* fd for output */
145 SDL_Surface
*keypad
; /* the skin for the keypad */
146 SDL_Rect kp_rect
; /* portion of the skin to display - default all */
147 SDL_Surface
*font
; /* font to be used */
148 SDL_Rect font_rects
[96]; /* only printable chars */
150 /* each of the following board has two rectangles,
151 * [0] is the geometry relative to the keypad,
152 * [1] is the geometry relative to the whole screen
153 * we do not use the thumb_bd for these boards because here we need
154 * 2 rectangles for geometry
156 SDL_Rect kp_msg
[2]; /* incoming msg, relative to kpad */
157 struct board
*bd_msg
;
159 SDL_Rect kp_edit
[2]; /* edit user input */
160 struct board
*bd_edit
;
162 SDL_Rect kp_dialed
[2]; /* dialed number */
163 struct board
*bd_dialed
;
165 /* other boards are one associated with the source windows
166 * above the keypad in the layout, we only have the geometry
167 * relative to the whole screen
169 struct thumb_bd thumb_bd_array
[MAX_VIDEO_SOURCES
];
171 /* variable-size array mapping keypad regions to functions */
172 int kp_size
, kp_used
;
173 struct keypad_entry
*kp
;
175 struct display_window win
[WIN_MAX
];
178 /*! \brief free the resources in struct gui_info and the descriptor itself.
179 * Return NULL so we can assign the value back to the descriptor in case.
181 static struct gui_info
*cleanup_sdl(struct gui_info
*gui
, int device_num
)
188 /* unload font file */
190 SDL_FreeSurface(gui
->font
);
197 SDL_FreeSurface(gui
->keypad
);
202 /* uninitialize the SDL environment */
203 for (i
= 0; i
< WIN_MAX
; i
++) {
205 SDL_FreeYUVOverlay(gui
->win
[i
].bmp
);
207 bzero(gui
, sizeof(gui
));
209 /* deallocates the space allocated for the keypad message boards */
211 delete_board(gui
->bd_dialed
);
213 delete_board(gui
->bd_msg
);
215 /* deallocates the space allocated for the thumbnail message boards */
216 for (i
= 0; i
< device_num
; i
++) {
217 if (gui
->thumb_bd_array
[i
].board
) /* may be useless */
218 delete_board(gui
->thumb_bd_array
[i
].board
);
226 /* messages to be displayed in the sources message boards
227 * below the source windows
230 /* costants defined to describe status of devices */
232 #define IS_SECONDARY 2
246 * Display video frames (from local or remote stream) using the SDL library.
247 * - Set the video mode to use the resolution specified by the codec context
248 * - Create a YUV Overlay to copy the frame into it;
249 * - After the frame is copied into the overlay, display it
251 * The size is taken from the configuration.
253 * 'out' is 0 for remote video, 1 for the local video
255 static void show_frame(struct video_desc
*env
, int out
)
257 AVPicture
*p_in
, p_out
;
258 struct fbuf_t
*b_in
, *b_out
;
260 struct gui_info
*gui
= env
->gui
;
265 if (out
== WIN_LOCAL
) { /* webcam/x11 to sdl */
267 b_out
= &env
->loc_dpy
;
269 } else if (out
== WIN_REMOTE
) {
270 /* copy input format from the decoding context */
272 if (env
->in
== NULL
) /* XXX should not happen - decoder not ready */
274 c
= env
->in
->dec_ctx
;
275 b_in
= &env
->in
->dec_out
;
276 b_in
->pix_fmt
= c
->pix_fmt
;
280 b_out
= &env
->rem_dpy
;
281 p_in
= (AVPicture
*)env
->in
->d_frame
;
283 int i
= out
-WIN_SRC1
;
284 b_in
= env
->out
.devices
[i
].dev_buf
;
288 b_out
= &env
->src_dpy
[i
];
290 bmp
= gui
->win
[out
].bmp
;
291 SDL_LockYUVOverlay(bmp
);
292 /* output picture info - this is sdl, YUV420P */
293 bzero(&p_out
, sizeof(p_out
));
294 p_out
.data
[0] = bmp
->pixels
[0];
295 p_out
.data
[1] = bmp
->pixels
[1];
296 p_out
.data
[2] = bmp
->pixels
[2];
297 p_out
.linesize
[0] = bmp
->pitches
[0];
298 p_out
.linesize
[1] = bmp
->pitches
[1];
299 p_out
.linesize
[2] = bmp
->pitches
[2];
301 my_scale(b_in
, p_in
, b_out
, &p_out
);
303 /* lock to protect access to Xlib by different threads. */
304 SDL_DisplayYUVOverlay(bmp
, &gui
->win
[out
].rect
);
305 SDL_UnlockYUVOverlay(bmp
);
309 * Identifiers for regions of the main window.
310 * Values between 0 and 127 correspond to ASCII characters.
311 * The corresponding strings to be used in the skin comment section
312 * are defined in gui_key_map.
315 /* answer/close functions */
320 KEY_AUTOANSWER
= 131,
322 KEY_LOCALVIDEO
= 133,
323 KEY_REMOTEVIDEO
= 134,
326 /* sensitive areas for the various text windows */
327 KEY_MESSAGEBOARD
= 140,
328 KEY_DIALEDBOARD
= 141,
331 KEY_GUI_CLOSE
= 199, /* close gui */
332 /* regions of the skin - displayed area, fonts, etc.
333 * XXX NOTE these are not sensitive areas.
335 KEY_KEYPAD
= 200, /* the keypad - default to the whole image */
336 KEY_FONT
= 201, /* the font. Maybe not really useful */
337 KEY_MESSAGE
= 202, /* area for incoming messages */
338 KEY_DIALED
= 203, /* area for dialed numbers */
339 KEY_EDIT
= 204, /* area for editing user input */
341 #ifdef notyet /* XXX for future implementation */
342 KEY_AUDIO_SRCS
= 210,
343 /*indexes between 210 and 219 (or more) have been reserved for the "keys"
344 associated with the audio device markers, clicking on these markers
345 will change the source device for audio output */
348 /* Keys related to video sources */
349 KEY_FREEZE
= 220, /* freeze the incoming video */
350 KEY_CAPTURE
= 221, /* capture the whole SDL window as a picture */
352 /*indexes between 231 and 239 have been reserved for the "keys"
353 associated with the device thumbnails, clicking on these pictures
354 will change the source device for primary or secondary (PiP) video output*/
355 KEY_SRCS_WIN
= 231, /* till 239 */
356 /* areas outside the keypad - simulated */
357 KEY_OUT_OF_KEYPAD
= 241,
360 KEY_RESET
= 253, /* the 'reset' keyword */
361 KEY_NONE
= 254, /* invalid area */
362 KEY_DIGIT_BACKGROUND
= 255, /* other areas within the keypad */
366 * Handlers for the various keypad functions
369 /* accumulate digits, possibly call dial if in connected mode */
370 static void keypad_digit(struct video_desc
*env
, int digit
)
372 if (env
->owner
) { /* we have a call, send the digit */
373 struct ast_frame f
= { AST_FRAME_DTMF
, 0 };
376 ast_queue_frame(env
->owner
, &f
);
377 } else { /* no call, accumulate digits */
378 char buf
[2] = { digit
, '\0' };
379 if (env
->gui
->bd_msg
) /* XXX not strictly necessary ... */
380 print_message(env
->gui
->bd_msg
, buf
);
384 /* function used to toggle on/off the status of some variables */
385 static char *keypad_toggle(struct video_desc
*env
, int index
)
387 ast_log(LOG_WARNING
, "keypad_toggle(%i) called\n", index
);
390 case KEY_SENDVIDEO
: /* send or do not send video */
391 env
->out
.sendvideo
= !env
->out
.sendvideo
;
394 case KEY_PIP
: /* enable or disable Picture in Picture */
395 env
->out
.picture_in_picture
= !env
->out
.picture_in_picture
;
398 case KEY_MUTE
: /* send or do not send audio */
399 ast_cli_command(env
->gui
->outfd
, "console mute toggle");
402 case KEY_FREEZE
: /* freeze/unfreeze the incoming frames */
403 env
->frame_freeze
= !env
->frame_freeze
;
407 case KEY_AUTOANSWER
: {
408 struct chan_oss_pvt
*o
= find_desc(oss_active
);
409 o
->autoanswer
= !o
->autoanswer
;
417 char *console_do_answer(int fd
);
419 * Function called when the pick up button is pressed
420 * perform actions according the channel status:
422 * - if no one is calling us and no digits was pressed,
423 * the operation have no effects,
424 * - if someone is calling us we answer to the call.
425 * - if we have no call in progress and we pressed some
426 * digit, send the digit to the console.
428 static void keypad_pick_up(struct video_desc
*env
)
430 struct gui_info
*gui
= env
->gui
;
432 ast_log(LOG_WARNING
, "keypad_pick_up called\n");
434 if (env
->owner
) { /* someone is calling us, just answer */
435 ast_cli_command(gui
->outfd
, "console answer");
436 } else { /* we have someone to call */
438 const char *who
= ast_skip_blanks(read_message(gui
->bd_msg
));
439 buf
[sizeof(buf
) - 1] = '\0';
440 snprintf(buf
, sizeof(buf
), "console dial %s", who
);
441 ast_log(LOG_WARNING
, "doing <%s>\n", buf
);
442 print_message(gui
->bd_dialed
, "\n");
443 print_message(gui
->bd_dialed
, who
);
444 reset_board(gui
->bd_msg
);
445 ast_cli_command(gui
->outfd
, buf
);
449 #if 0 /* still unused */
451 * As an alternative to SDL_TTF, we can simply load the font from
452 * an image and blit characters on the background of the GUI.
454 * To generate a font we can use the 'fly' command with the
455 * following script (3 lines with 32 chars each)
460 string 255,255,255, 0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
461 string 255,255,255, 0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
462 string 255,255,255, 0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
467 /* Print given text on the gui */
468 static int gui_output(struct video_desc
*env
, const char *text
)
470 return 1; /* error, not supported */
474 static int video_geom(struct fbuf_t
*b
, const char *s
);
475 static void sdl_setup(struct video_desc
*env
);
476 static int kp_match_area(const struct keypad_entry
*e
, int x
, int y
);
478 static void set_drag(struct drag_info
*drag
, int x
, int y
, enum drag_window win
)
482 drag
->drag_window
= win
;
485 static int update_device_info(struct video_desc
*env
, int i
)
487 reset_board(env
->gui
->thumb_bd_array
[i
].board
);
488 print_message(env
->gui
->thumb_bd_array
[i
].board
,
489 src_msgs
[env
->out
.devices
[i
].status_index
]);
493 /*! \brief Changes the video output (local video) source, controlling if
494 * it is already using that video device,
495 * and switching the correct fields of env->out.
496 * grabbers are always open and saved in the device table.
497 * The secondary or the primary device can be changed,
498 * according to the "button" parameter:
499 * the primary device is changed if button = SDL_BUTTON_LEFT;
500 * the secondary device is changed if button = not SDL_BUTTON_LEFT;
502 * the correct message boards of the sources are also updated
503 * with the new status
505 * \param env = pointer to the video environment descriptor
506 * \param index = index of the device the caller wants to use are primary or secondary device
507 * \param button = button clicked on the mouse
509 * returns 0 on success,
512 static int switch_video_out(struct video_desc
*env
, int index
, Uint8 button
)
514 int *p
; /* pointer to the index of the device to select */
516 if (index
>= env
->out
.device_num
) {
517 ast_log(LOG_WARNING
, "no devices\n");
520 /* select primary or secondary */
521 p
= (button
== SDL_BUTTON_LEFT
) ? &env
->out
.device_primary
:
522 &env
->out
.device_secondary
;
523 /* controls if the device is already selected */
525 ast_log(LOG_WARNING
, "device %s already selected\n", env
->out
.devices
[index
].name
);
528 ast_log(LOG_WARNING
, "switching to %s...\n", env
->out
.devices
[index
].name
);
530 if (env
->out
.devices
[index
].grabber
) {
531 /* we also have to update the messages in the source
532 message boards below the source windows */
533 /* first we update the board of the previous source */
534 if (p
== &env
->out
.device_primary
)
535 env
->out
.devices
[*p
].status_index
&= ~IS_PRIMARY
;
537 env
->out
.devices
[*p
].status_index
&= ~IS_SECONDARY
;
538 update_device_info(env
, *p
);
539 /* update the index used as primary or secondary */
541 ast_log(LOG_WARNING
, "done\n");
542 /* then we update the board of the new primary or secondary source */
543 if (p
== &env
->out
.device_primary
)
544 env
->out
.devices
[*p
].status_index
|= IS_PRIMARY
;
546 env
->out
.devices
[*p
].status_index
|= IS_SECONDARY
;
547 update_device_info(env
, *p
);
550 /* device is off, just do nothing */
551 ast_log(LOG_WARNING
, "device is down\n");
555 /*! \brief tries to switch the state of a device from on to off or off to on
556 * we also have to update the status of the device and the correct message board
558 * \param index = the device that must be turned on or off
559 * \param env = pointer to the video environment descriptor
562 * - 0 on falure switching from off to on
563 * - 1 on success in switching from off to on
564 * - 2 on success in switching from on to off
566 static int turn_on_off(int index
, struct video_desc
*env
)
568 struct video_device
*p
= &env
->out
.devices
[index
];
570 if (index
>= env
->out
.device_num
) {
571 ast_log(LOG_WARNING
, "no devices\n");
575 if (!p
->grabber
) { /* device off */
576 void *g_data
; /* result of grabber_open() */
580 /* see if the device can be used by one of the existing drivers */
581 for (i
= 0; (g
= console_grabbers
[i
]); i
++) {
582 /* try open the device */
583 g_data
= g
->open(p
->name
, &env
->out
.loc_src_geometry
, env
->out
.fps
);
584 if (!g_data
) /* no luck, try the next driver */
587 p
->grabber_data
= g_data
;
588 /* update the status of the source */
589 p
->status_index
|= IS_ON
;
590 /* print the new message in the message board */
591 update_device_info(env
, index
);
592 return 1; /* open succeded */
594 return 0; /* failure */
596 /* the grabber must be closed */
597 p
->grabber_data
= p
->grabber
->close(p
->grabber_data
);
599 /* dev_buf is already freed by grabber->close() */
601 /* update the status of the source */
602 p
->status_index
&= ~IS_ON
;
603 /* print the new message in the message board */
604 update_device_info(env
, index
);
605 return 2; /* closed */
610 * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
611 * index value and calling the right callback.
613 * x, y are referred to the upper left corner of the main SDL window.
615 static void handle_mousedown(struct video_desc
*env
, SDL_MouseButtonEvent button
)
617 uint8_t index
= KEY_OUT_OF_KEYPAD
; /* the key or region of the display we clicked on */
618 struct gui_info
*gui
= env
->gui
;
620 int i
; /* integer variable used as iterator */
622 int x
; /* integer variable usable as a container */
624 /* total width of source device thumbnails */
625 int src_wins_tot_w
= env
->out
.device_num
*(SRC_WIN_W
+BORDER
)+BORDER
;
627 /* x coordinate of the center of the keypad */
628 int x0
= MAX(env
->rem_dpy
.w
+gui
->keypad
->w
/2+2*BORDER
, src_wins_tot_w
/2);
631 ast_log(LOG_WARNING
, "event %d %d have %d/%d regions at %p\n",
632 button
.x
, button
.y
, gui
->kp_used
, gui
->kp_size
, gui
->kp
);
634 /* for each mousedown we end previous drag */
635 gui
->drag
.drag_window
= DRAG_NONE
;
637 /* define keypad boundary */
638 /* XXX this should be extended for clicks on different audio device markers */
639 if (button
.y
>= (env
->out
.device_num
? SRC_WIN_H
+2*BORDER
+SRC_MSG_BD_H
: 0)) {
640 /* if control reaches this point this means that the clicked point is
641 below the row of the additional sources windows*/
642 /* adjust the y coordinate as if additional devices windows were not present */
643 button
.y
-= (env
->out
.device_num
? SRC_WIN_H
+2*BORDER
+SRC_MSG_BD_H
: 0);
644 if (button
.y
< BORDER
)
645 index
= KEY_OUT_OF_KEYPAD
;
646 else if (button
.y
>= MAX(MAX(env
->rem_dpy
.h
, env
->loc_dpy
.h
), gui
->keypad
->h
))
647 index
= KEY_OUT_OF_KEYPAD
;
648 else if (button
.x
< x0
- gui
->keypad
->w
/2 - BORDER
- env
->rem_dpy
.w
)
649 index
= KEY_OUT_OF_KEYPAD
;
650 else if (button
.x
< x0
- gui
->keypad
->w
/2 - BORDER
)
652 else if (button
.x
< x0
- gui
->keypad
->w
/2)
653 index
= KEY_OUT_OF_KEYPAD
;
654 else if (button
.x
>= x0
+ gui
->keypad
->w
/2 + BORDER
+ env
->loc_dpy
.w
)
655 index
= KEY_OUT_OF_KEYPAD
;
656 else if (button
.x
>= x0
+ gui
->keypad
->w
/2 + BORDER
)
658 else if (button
.x
>= x0
+ gui
->keypad
->w
/2)
659 index
= KEY_OUT_OF_KEYPAD
;
661 /* we have to calculate the first coordinate
662 inside the keypad before calling the kp_match_area*/
663 int x_keypad
= button
.x
- (x0
- gui
->keypad
->w
/2);
664 /* find the key clicked (if one was clicked) */
665 for (i
= 0; i
< gui
->kp_used
; i
++) {
666 if (kp_match_area(&gui
->kp
[i
],x_keypad
, button
.y
- BORDER
)) {
667 index
= gui
->kp
[i
].c
;
672 } else if (button
.y
< BORDER
) {
673 index
= KEY_OUT_OF_KEYPAD
;
674 } else { /* we are in the thumbnail area */
675 x
= x0
- src_wins_tot_w
/2 + BORDER
;
676 if (button
.y
>= BORDER
+ SRC_WIN_H
)
677 index
= KEY_OUT_OF_KEYPAD
;
678 else if (button
.x
< x
)
679 index
= KEY_OUT_OF_KEYPAD
;
680 else if (button
.x
< x
+ src_wins_tot_w
- BORDER
) {
681 /* note that the additional device windows
682 are numbered from left to right
683 starting from 0, with a maximum of 8, the index associated on a click is:
684 KEY_SRCS_WIN + number_of_the_window */
685 for (i
= 1; i
<= env
->out
.device_num
; i
++) {
686 if (button
.x
< x
+i
*(SRC_WIN_W
+BORDER
)-BORDER
) {
687 index
= KEY_SRCS_WIN
+i
-1;
689 } else if (button
.x
< x
+i
*(SRC_WIN_W
+BORDER
)) {
690 index
= KEY_OUT_OF_KEYPAD
;
695 index
= KEY_OUT_OF_KEYPAD
;
698 /* exec the function */
699 if (index
< 128) { /* surely clicked on the keypad, don't care which key */
700 keypad_digit(env
, index
);
704 else if (index
>= KEY_SRCS_WIN
&& index
< KEY_SRCS_WIN
+env
->out
.device_num
) {
705 index
-= KEY_SRCS_WIN
; /* index of the window, equal to the device index in the table */
706 /* if one of the additional device windows is clicked with
707 left or right mouse button, we have to switch to that device */
708 if (button
.button
== SDL_BUTTON_RIGHT
|| button
.button
== SDL_BUTTON_LEFT
) {
709 switch_video_out(env
, index
, button
.button
);
712 /* turn on or off the devices selectively with other mouse buttons */
714 int ret
= turn_on_off(index
, env
);
715 /* print a message according to what happened */
717 ast_log(LOG_WARNING
, "unable to turn on device %s\n",
718 env
->out
.devices
[index
].name
);
720 ast_log(LOG_WARNING
, "device %s changed state to on\n",
721 env
->out
.devices
[index
].name
);
723 ast_log(LOG_WARNING
, "device %s changed state to off\n",
724 env
->out
.devices
[index
].name
);
729 /* XXX for future implementation
730 else if (click on audio source marker)
731 change audio source device
735 /* answer/close function */
740 ast_cli_command(gui
->outfd
, "console hangup");
743 /* other functions */
744 case KEY_MUTE
: /* send or not send the audio */
746 case KEY_SENDVIDEO
: /* send or not send the video */
747 case KEY_PIP
: /* activate/deactivate picture in picture mode */
748 case KEY_FREEZE
: /* freeze/unfreeze the incoming video */
749 keypad_toggle(env
, index
);
754 case KEY_REMOTEVIDEO
:
757 #ifdef notyet /* XXX for future implementations */
762 case KEY_MESSAGEBOARD
:
763 if (button
.button
== SDL_BUTTON_LEFT
)
764 set_drag(&gui
->drag
, button
.x
, button
.y
, DRAG_MESSAGE
);
767 /* press outside the keypad. right increases size, center decreases, left drags */
770 if (button
.button
== SDL_BUTTON_LEFT
) {
771 /* values used to find the position of the picture in picture (if present) */
772 int pip_loc_x
= (double)env
->out
.pip_x
/env
->enc_in
.w
* env
->loc_dpy
.w
;
773 int pip_loc_y
= (double)env
->out
.pip_y
/env
->enc_in
.h
* env
->loc_dpy
.h
;
774 /* check if picture in picture is active and the click was on it */
775 if (index
== KEY_LOC_DPY
&& env
->out
.picture_in_picture
&&
776 button
.x
>= x0
+gui
->keypad
->w
/2+BORDER
+pip_loc_x
&&
777 button
.x
< x0
+gui
->keypad
->w
/2+BORDER
+pip_loc_x
+env
->loc_dpy
.w
/3 &&
778 button
.y
>= BORDER
+pip_loc_y
&&
779 button
.y
< BORDER
+pip_loc_y
+env
->loc_dpy
.h
/3) {
780 /* set the y cordinate to his previous value */
781 button
.y
+= (env
->out
.device_num
? SRC_WIN_H
+2*BORDER
+SRC_MSG_BD_H
: 0);
782 /* starts dragging the picture inside the picture */
783 set_drag(&gui
->drag
, button
.x
, button
.y
, DRAG_PIP
);
785 else if (index
== KEY_LOC_DPY
) {
786 /* set the y cordinate to his previous value */
787 button
.y
+= (env
->out
.device_num
? SRC_WIN_H
+2*BORDER
+SRC_MSG_BD_H
: 0);
788 /* click in the local display, but not on the PiP */
789 set_drag(&gui
->drag
, button
.x
, button
.y
, DRAG_LOCAL
);
794 struct fbuf_t
*fb
= index
== KEY_LOC_DPY
? &env
->loc_dpy
: &env
->rem_dpy
;
795 sprintf(buf
, "%c%dx%d", button
.button
== SDL_BUTTON_RIGHT
? '>' : '<',
799 /* writes messages in the source boards, those can be
800 modified during the execution, because of the events
801 this must be done here, otherwise the status of sources will not be
802 shown after sdl_setup */
803 for (i
= 0; i
< env
->out
.device_num
; i
++) {
804 update_device_info(env
, i
);
806 /* we also have to refresh other boards,
807 to avoid messages to disappear after video resize */
808 print_message(gui
->bd_msg
, " \b");
809 print_message(gui
->bd_dialed
, " \b");
812 case KEY_OUT_OF_KEYPAD
:
813 ast_log(LOG_WARNING
, "nothing clicked, coordinates: %d, %d\n", button
.x
, button
.y
);
816 case KEY_DIGIT_BACKGROUND
:
820 ast_log(LOG_WARNING
, "function not yet defined %i\n", index
);
825 * Handle SDL_KEYDOWN type event, put the key pressed
826 * in the dial buffer or in the text-message buffer,
827 * depending on the text_mode variable value.
829 * key is the SDLKey structure corresponding to the key pressed.
830 * Note that SDL returns modifiers (ctrl, shift, alt) as independent
831 * information so the key itself is not enough and we need to
832 * use a translation table, below - one line per entry,
833 * plain, shift, ctrl, ... using the first char as key.
835 static const char *us_kbd_map
[] = {
836 "`~", "1!", "2@", "3#", "4$", "5%", "6^",
837 "7&", "8*", "9(", "0)", "-_", "=+", "[{",
838 "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
843 static char map_key(SDL_keysym
*ks
)
845 const char *s
, **p
= us_kbd_map
;
848 if (c
== '\r') /* map cr into lf */
850 if (c
>= SDLK_NUMLOCK
&& c
<= SDLK_COMPOSE
)
851 return 0; /* only a modifier */
854 while ((s
= *p
) && s
[0] != c
)
856 if (s
) { /* see if we have a modifier and a chance to use it */
857 int l
= strlen(s
), mod
= 0;
859 mod
|= (ks
->mod
& KMOD_SHIFT
) ? 1 : 0;
861 mod
|= (ks
->mod
& KMOD_CTRL
) ? 2 : 0;
863 mod
|= (ks
->mod
& KMOD_ALT
) ? 4 : 0;
866 if (ks
->mod
& (KMOD_CAPS
|KMOD_SHIFT
) && c
>= 'a' && c
<='z')
871 static void handle_keyboard_input(struct video_desc
*env
, SDL_keysym
*ks
)
873 char buf
[2] = { map_key(ks
), '\0' };
874 struct gui_info
*gui
= env
->gui
;
875 if (buf
[0] == 0) /* modifier ? */
877 switch (gui
->kb_output
) {
880 case KO_INPUT
: /* to be completed */
884 print_message(gui
->bd_msg
, buf
);
885 if (buf
[0] == '\r' || buf
[0] == '\n') {
891 case KO_DIALED
: /* to be completed */
898 static void grabber_move(struct video_device
*, int dx
, int dy
);
900 int compute_drag(int *start
, int end
, int magnifier
);
901 int compute_drag(int *start
, int end
, int magnifier
)
903 int delta
= end
- *start
;
905 /* add a small quadratic term */
906 delta
+= delta
* delta
* (delta
> 0 ? 1 : -1 )/100;
907 delta
*= POLARITY
* magnifier
;
913 /*! \brief This function moves the picture in picture,
914 * controlling the limits of the containing buffer
915 * to avoid problems deriving from going through the limits.
917 * \param env = pointer to the descriptor of the video environment
918 * \param dx = the variation of the x position
919 * \param dy = the variation of the y position
921 static void pip_move(struct video_desc
* env
, int dx
, int dy
) {
922 int new_pip_x
= env
->out
.pip_x
+dx
;
923 int new_pip_y
= env
->out
.pip_y
+dy
;
924 /* going beyond the left borders */
927 /* going beyond the right borders */
928 else if (new_pip_x
> env
->enc_in
.w
- env
->enc_in
.w
/3)
929 new_pip_x
= env
->enc_in
.w
- env
->enc_in
.w
/3;
930 /* going beyond the top borders */
933 /* going beyond the bottom borders */
934 else if (new_pip_y
> env
->enc_in
.h
- env
->enc_in
.h
/3)
935 new_pip_y
= env
->enc_in
.h
- env
->enc_in
.h
/3;
936 env
->out
.pip_x
= new_pip_x
;
937 env
->out
.pip_y
= new_pip_y
;
941 * I am seeing some kind of deadlock or stall around
942 * SDL_PumpEvents() while moving the window on a remote X server
943 * (both xfree-4.4.0 and xorg 7.2)
944 * and windowmaker. It is unclear what causes it.
947 /*! \brief refresh the screen, and also grab a bunch of events.
949 static void eventhandler(struct video_desc
*env
, const char *caption
)
951 struct gui_info
*gui
= env
->gui
;
952 struct drag_info
*drag
;
955 SDL_Event ev
[N_EVENTS
];
961 SDL_WM_SetCaption(caption
, NULL
);
963 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
964 while ( (n
= SDL_PeepEvents(ev
, N_EVENTS
, SDL_GETEVENT
, SDL_ALLEVENTS
)) > 0) {
965 for (i
= 0; i
< n
; i
++) {
967 ast_log(LOG_WARNING
, "------ event %d at %d %d\n",
968 ev
[i
].type
, ev
[i
].button
.x
, ev
[i
].button
.y
);
970 switch (ev
[i
].type
) {
972 ast_log(LOG_WARNING
, "------ event %d at %d %d\n",
973 ev
[i
].type
, ev
[i
].button
.x
, ev
[i
].button
.y
);
976 case SDL_ACTIVEEVENT
:
977 #if 0 /* do not react, we don't want to die because the window is minimized */
978 if (ev
[i
].active
.gain
== 0 && ev
[i
].active
.state
& SDL_APPACTIVE
) {
979 ast_log(LOG_WARNING
, "/* somebody has killed us ? */");
980 ast_cli_command(gui
->outfd
, "stop now");
985 case SDL_KEYUP
: /* ignore, for the time being */
989 handle_keyboard_input(env
, &ev
[i
].key
.keysym
);
992 case SDL_MOUSEMOTION
:
993 case SDL_MOUSEBUTTONUP
:
994 if (drag
->drag_window
== DRAG_LOCAL
&& env
->out
.device_num
) {
995 /* move the capture source */
996 int dx
= compute_drag(&drag
->x_start
, ev
[i
].motion
.x
, 3);
997 int dy
= compute_drag(&drag
->y_start
, ev
[i
].motion
.y
, 3);
998 grabber_move(&env
->out
.devices
[env
->out
.device_primary
], dx
, dy
);
999 } else if (drag
->drag_window
== DRAG_PIP
) {
1000 /* move the PiP image inside the frames of the enc_in buffers */
1001 int dx
= ev
[i
].motion
.x
- drag
->x_start
;
1002 int dy
= ev
[i
].motion
.y
- drag
->y_start
;
1003 /* dx and dy value are directly applied to env->out.pip_x and
1004 env->out.pip_y, so they must work as if the format was cif */
1005 dx
= (double)dx
*env
->enc_in
.w
/env
->loc_dpy
.w
;
1006 dy
= (double)dy
*env
->enc_in
.h
/env
->loc_dpy
.h
;
1007 /* sets starts to a new value */
1008 drag
->x_start
= ev
[i
].motion
.x
;
1009 drag
->y_start
= ev
[i
].motion
.y
;
1010 /* ast_log(LOG_WARNING, "moving: %d, %d\n", dx, dy); */
1011 pip_move(env
, dx
, dy
);
1012 } else if (drag
->drag_window
== DRAG_MESSAGE
) {
1013 /* scroll up/down the window */
1014 int dy
= compute_drag(&drag
->y_start
, ev
[i
].motion
.y
, 1);
1015 move_message_board(gui
->bd_msg
, dy
);
1017 if (ev
[i
].type
== SDL_MOUSEBUTTONUP
)
1018 drag
->drag_window
= DRAG_NONE
;
1020 case SDL_MOUSEBUTTONDOWN
:
1021 handle_mousedown(env
, ev
[i
].button
);
1027 struct timeval b
, a
= ast_tvnow();
1029 //SDL_Lock_EventThread();
1032 i
= ast_tvdiff_ms(b
, a
);
1034 fprintf(stderr
, "-------- SDL_PumpEvents took %dms\n", i
);
1035 //SDL_Unlock_EventThread();
1039 static SDL_Surface
*load_image(const char *file
)
1043 #ifdef HAVE_SDL_IMAGE
1044 temp
= IMG_Load(file
);
1046 temp
= SDL_LoadBMP(file
);
1049 fprintf(stderr
, "Unable to load image %s: %s\n",
1050 file
, SDL_GetError());
1054 static void keypad_setup(struct gui_info
*gui
, const char *kp_file
);
1056 /* TODO: consistency checks, check for bpp, widht and height */
1057 /* Init the mask image used to grab the action. */
1058 static struct gui_info
*gui_init(const char *keypad_file
, const char *font
)
1060 struct gui_info
*gui
= ast_calloc(1, sizeof(*gui
));
1064 /* initialize keypad status */
1065 gui
->kb_output
= KO_MESSAGE
; /* XXX temp */
1066 gui
->drag
.drag_window
= DRAG_NONE
;
1069 keypad_setup(gui
, keypad_file
);
1070 if (gui
->keypad
== NULL
) /* no keypad, we are done */
1072 /* XXX load image */
1073 if (!ast_strlen_zero(font
)) {
1077 gui
->font
= load_image(font
);
1079 ast_log(LOG_WARNING
, "Unable to load font %s, no output available\n", font
);
1082 ast_log(LOG_WARNING
, "Loaded font %s\n", font
);
1083 /* XXX hardwired constants - 3 rows of 32 chars */
1084 r
= gui
->font_rects
;
1087 for (i
= 0; i
< 96; r
++, i
++) {
1088 r
->x
= (i
% 32 ) * FONT_W
;
1089 r
->y
= (i
/ 32 ) * FONT_H
;
1095 gui
->outfd
= open ("/dev/null", O_WRONLY
); /* discard output, temporary */
1096 if (gui
->outfd
< 0) {
1097 ast_log(LOG_WARNING
, "Unable output fd\n");
1107 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
1108 static int set_win(SDL_Surface
*screen
, struct display_window
*win
, int fmt
,
1109 int w
, int h
, int x
, int y
)
1111 win
->bmp
= SDL_CreateYUVOverlay(w
, h
, fmt
, screen
);
1112 if (win
->bmp
== NULL
)
1113 return -1; /* error */
1121 static int keypad_cfg_read(struct gui_info
*gui
, const char *val
);
1123 static void keypad_setup(struct gui_info
*gui
, const char *kp_file
)
1127 const char region
[] = "region";
1128 int reg_len
= strlen(region
);
1133 gui
->keypad
= load_image(kp_file
);
1136 /* now try to read the keymap from the file. */
1137 fd
= fopen(kp_file
, "r");
1139 ast_log(LOG_WARNING
, "fail to open %s\n", kp_file
);
1143 * If the keypad image has a comment field, try to read
1144 * the button location from there. The block must start with
1145 * a comment (or empty) line, and continue with entries like:
1146 * region = token shape x0 y0 x1 y1 h
1148 * (basically, lines have the same format as config file entries).
1149 * You can add it to a jpeg file using wrjpgcom
1151 while (fgets(buf
, sizeof(buf
), fd
)) {
1154 if (!strstr(buf
, region
)) { /* no keyword yet */
1155 if (!in_comment
) /* still waiting for initial comment block */
1160 if (!in_comment
) { /* first keyword, reset previous entries */
1161 keypad_cfg_read(gui
, "reset");
1164 s
= ast_skip_blanks(buf
);
1166 if (memcmp(s
, region
, reg_len
))
1167 break; /* keyword not found */
1168 s
= ast_skip_blanks(s
+ reg_len
); /* space between token and '=' */
1169 if (*s
++ != '=') /* missing separator */
1171 if (*s
== '>') /* skip '>' if present */
1173 keypad_cfg_read(gui
, ast_skip_blanks(s
));
1178 struct board
*board_setup(SDL_Surface
*screen
, SDL_Rect
*dest
,
1179 SDL_Surface
*font
, SDL_Rect
*font_rects
);
1181 /*! \brief initialize the boards we have in the keypad */
1182 static void init_board(struct gui_info
*gui
, struct board
**dst
, SDL_Rect
*r
, int dx
, int dy
)
1184 if (r
[0].w
== 0 || r
[0].h
== 0)
1185 return; /* not available */
1186 r
[1] = r
[0]; /* copy geometry */
1187 r
[1].x
+= dx
; /* add offset of main window */
1189 if (*dst
== NULL
) { /* initial call */
1190 *dst
= board_setup(gui
->screen
, &r
[1], gui
->font
, gui
->font_rects
);
1192 /* call a refresh */
1198 * SDL is not very robust on error handling, so we need to trap ourselves
1199 * at least the most obvious failure conditions, e.g. a bad SDL_WINDOWID.
1200 * As of sdl-1.2.13, SDL_SetVideoMode crashes with bad parameters, so
1201 * we need to do the explicit X calls to make sure the window is correct.
1202 * And around these calls, we must trap X errors.
1204 static int my_x_handler(Display
*d
, XErrorEvent
*e
)
1206 ast_log(LOG_WARNING
, "%s error_code %d\n", __FUNCTION__
, e
->error_code
);
1209 #endif /* HAVE_X11 */
1211 /*! \brief [re]set the main sdl window, useful in case of resize.
1212 * We can tell the first from subsequent calls from the value of
1213 * env->gui, which is NULL the first time.
1215 static void sdl_setup(struct video_desc
*env
)
1217 int dpy_fmt
= SDL_IYUV_OVERLAY
; /* YV12 causes flicker in SDL */
1218 int depth
, maxw
, maxh
;
1219 const SDL_VideoInfo
*info
;
1220 int kp_w
= 0, kp_h
= 0; /* keypad width and height */
1221 struct gui_info
*gui
= env
->gui
;
1223 /* Some helper variables used for filling the SDL window */
1224 int x0
; /* the x coordinate of the center of the keypad */
1225 int x1
; /* userful for calculating of the size of the parent window */
1226 int y0
; /* y coordinate of the keypad, the remote window and the local window */
1227 int src_wins_tot_w
; /* total width of the source windows */
1229 int x
; /* useful for the creation of the source windows; */
1232 const char *e
= getenv("SDL_WINDOWID");
1234 if (!ast_strlen_zero(e
)) {
1235 XWindowAttributes a
;
1236 int (*old_x_handler
)(Display
*d
, XErrorEvent
*e
) = XSetErrorHandler(my_x_handler
);
1237 Display
*d
= XOpenDisplay(getenv("DISPLAY"));
1239 int success
= w
? XGetWindowAttributes(d
, w
, &a
) : 0;
1241 XSetErrorHandler(old_x_handler
);
1243 ast_log(LOG_WARNING
, "%s error in window\n", __FUNCTION__
);
1249 * initialize the SDL environment. We have one large window
1250 * with local and remote video, and a keypad.
1251 * At the moment we arrange them statically, as follows:
1252 * - top row: thumbnails for local video sources;
1253 * - next row: message boards for local video sources
1254 * - on the left, the remote video;
1255 * - on the center, the keypad
1256 * - on the right, the local video
1257 * We need to read in the skin for the keypad before creating the main
1258 * SDL window, because the size is only known here.
1261 if (gui
== NULL
&& SDL_Init(SDL_INIT_VIDEO
)) {
1262 ast_log(LOG_WARNING
, "Could not initialize SDL - %s\n",
1264 /* again not fatal, just we won't display anything */
1267 info
= SDL_GetVideoInfo();
1268 /* We want at least 16bpp to support YUV overlays.
1269 * E.g with SDL_VIDEODRIVER = aalib the default is 8
1271 if (!info
|| !info
->vfmt
) {
1272 ast_log(LOG_WARNING
, "Bad SDL_GetVideoInfo - %s\n",
1276 depth
= info
->vfmt
->BitsPerPixel
;
1280 env
->gui
= gui
= gui_init(env
->keypad_file
, env
->keypad_font
);
1285 if (gui
->kp_rect
.w
> 0 && gui
->kp_rect
.h
> 0) {
1286 kp_w
= gui
->kp_rect
.w
;
1287 kp_h
= gui
->kp_rect
.h
;
1289 kp_w
= gui
->keypad
->w
;
1290 kp_h
= gui
->keypad
->h
;
1294 /* total width of the thumbnails */
1295 src_wins_tot_w
= env
->out
.device_num
*(SRC_WIN_W
+BORDER
)+BORDER
;
1297 /* x coordinate of the center of the keypad */
1298 x0
= MAX(env
->rem_dpy
.w
+kp_w
/2+2*BORDER
, src_wins_tot_w
/2);
1300 /* from center of the keypad to right border */
1301 x1
= MAX(env
->loc_dpy
.w
+kp_w
/2+2*BORDER
, src_wins_tot_w
/2);
1303 /* total width of the SDL window to create */
1306 /* total height of the mother window to create */
1307 maxh
= MAX( MAX(env
->rem_dpy
.h
, env
->loc_dpy
.h
), kp_h
)+2*BORDER
;
1308 maxh
+= env
->out
.device_num
? (2*BORDER
+SRC_WIN_H
+SRC_MSG_BD_H
) : 0;
1310 gui
->screen
= SDL_SetVideoMode(maxw
, maxh
, depth
, 0);
1312 ast_log(LOG_ERROR
, "SDL: could not set video mode - exiting\n");
1318 * Annoying as it may be, if SDL_WINDOWID is set, SDL does
1319 * not grab keyboard/mouse events or expose or other stuff,
1320 * and it does not handle resize either.
1321 * So we need to implement workarounds here.
1324 /* First, handle the event mask */
1325 XWindowAttributes attr
;
1328 Display
*SDL_Display
;
1331 const char *e
= getenv("SDL_WINDOWID");
1332 if (ast_strlen_zero(e
)) /* no external window, don't bother doing this */
1334 SDL_VERSION(&info
.version
); /* it is important to set the version */
1335 if (SDL_GetWMInfo(&info
) != 1) {
1336 fprintf(stderr
, "no wm info\n");
1339 SDL_Display
= info
.info
.x11
.display
;
1340 if (SDL_Display
== NULL
)
1342 win
= info
.info
.x11
.window
;
1345 * A list of events we want.
1346 * Leave ResizeRedirectMask to the parent.
1348 want
= KeyPressMask
| KeyReleaseMask
| ButtonPressMask
|
1349 ButtonReleaseMask
| EnterWindowMask
|
1350 LeaveWindowMask
| PointerMotionMask
|
1352 Button2MotionMask
| Button3MotionMask
|
1353 Button4MotionMask
| Button5MotionMask
|
1354 ButtonMotionMask
| KeymapStateMask
|
1355 ExposureMask
| VisibilityChangeMask
|
1356 StructureNotifyMask
| /* ResizeRedirectMask | */
1357 SubstructureNotifyMask
| SubstructureRedirectMask
|
1358 FocusChangeMask
| PropertyChangeMask
|
1359 ColormapChangeMask
| OwnerGrabButtonMask
;
1361 bzero(&attr
, sizeof(attr
));
1362 XGetWindowAttributes(SDL_Display
, win
, &attr
);
1364 /* the following events can be delivered only to one client.
1365 * So check which ones are going to someone else, and drop
1366 * them from our request.
1369 /* ev are the events for a single recipient */
1370 long ev
= ButtonPressMask
| ResizeRedirectMask
|
1371 SubstructureRedirectMask
;
1372 ev
&= (attr
.all_event_masks
& ~attr
.your_event_mask
);
1373 /* now ev contains 1 for single-recipient events owned by others.
1374 * We must clear those bits in 'want'
1375 * and then add the bits in 'attr.your_event_mask' to 'want'
1378 want
|= attr
.your_event_mask
;
1380 XSelectInput(SDL_Display
, win
, want
);
1382 /* Second, handle resize.
1383 * We do part of the things that X11Resize does,
1384 * but also generate a ConfigureNotify event so
1385 * the owner of the window has a chance to do something
1388 XResizeWindow(SDL_Display
, win
, maxw
, maxh
);
1390 XConfigureEvent ce
= {
1391 .type
= ConfigureNotify
,
1393 .send_event
= 1, /* TRUE */
1394 .display
= SDL_Display
,
1403 .override_redirect
= 0 };
1404 XSendEvent(SDL_Display
, win
, 1 /* TRUE */, StructureNotifyMask
, (XEvent
*)&ce
);
1407 #endif /* HAVE_X11 */
1409 y0
= env
->out
.device_num
? (3*BORDER
+SRC_WIN_H
+SRC_MSG_BD_H
) : BORDER
;
1411 SDL_WM_SetCaption("Asterisk console Video Output", NULL
);
1413 /* intialize the windows for local and remote video */
1414 if (set_win(gui
->screen
, &gui
->win
[WIN_REMOTE
], dpy_fmt
,
1415 env
->rem_dpy
.w
, env
->rem_dpy
.h
, x0
-kp_w
/2-BORDER
-env
->rem_dpy
.w
, y0
))
1417 /* unfreeze incoming frames if set (to avoid showing nothing) */
1418 env
->frame_freeze
= 0;
1420 if (set_win(gui
->screen
, &gui
->win
[WIN_LOCAL
], dpy_fmt
,
1421 env
->loc_dpy
.w
, env
->loc_dpy
.h
,
1422 x0
+kp_w
/2+BORDER
, y0
))
1425 /* initialize device_num source windows (thumbnails) and boards
1426 (for a maximum of 9 additional windows and boards) */
1427 x
= x0
- src_wins_tot_w
/2 + BORDER
;
1428 for (i
= 0; i
< env
->out
.device_num
; i
++){
1429 struct thumb_bd
*p
= &gui
->thumb_bd_array
[i
];
1430 if (set_win(gui
->screen
, &gui
->win
[i
+WIN_SRC1
], dpy_fmt
,
1431 SRC_WIN_W
, SRC_WIN_H
, x
+i
*(BORDER
+SRC_WIN_W
), BORDER
))
1433 /* set geometry for the rect for the message board of the device */
1434 p
->rect
.w
= SRC_WIN_W
;
1435 p
->rect
.h
= SRC_MSG_BD_H
;
1436 p
->rect
.x
= x
+i
*(BORDER
+SRC_WIN_W
);
1437 p
->rect
.y
= 2*BORDER
+SRC_WIN_H
;
1438 /* the white color is used as background */
1439 SDL_FillRect(gui
->screen
, &p
->rect
,
1440 SDL_MapRGB(gui
->screen
->format
, 255, 255, 255));
1441 /* if necessary, initialize boards for the sources */
1444 board_setup(gui
->screen
, &p
->rect
,
1445 gui
->font
, gui
->font_rects
);
1446 /* update board rect */
1447 SDL_UpdateRect(gui
->screen
, p
->rect
.x
, p
->rect
.y
, p
->rect
.w
, p
->rect
.h
);
1450 /* display the skin, but do not free it as we need it later to
1451 restore text areas and maybe sliders too */
1453 struct SDL_Rect
*dest
= &gui
->win
[WIN_KEYPAD
].rect
;
1454 struct SDL_Rect
*src
= (gui
->kp_rect
.w
> 0 && gui
->kp_rect
.h
> 0) ? & gui
->kp_rect
: NULL
;
1455 /* set the coordinates of the keypad relative to the main screen */
1456 dest
->x
= x0
-kp_w
/2;
1460 SDL_BlitSurface(gui
->keypad
, src
, gui
->screen
, dest
);
1461 init_board(gui
, &gui
->bd_msg
, gui
->kp_msg
, dest
->x
, dest
->y
);
1462 init_board(gui
, &gui
->bd_dialed
, gui
->kp_dialed
, dest
->x
, dest
->y
);
1463 SDL_UpdateRects(gui
->screen
, 1, dest
);
1468 /* free resources in case of errors */
1469 env
->gui
= cleanup_sdl(gui
, env
->out
.device_num
);
1473 * Functions to determine if a point is within a region. Return 1 if success.
1474 * First rotate the point, with
1475 * x' = (x - x0) * cos A + (y - y0) * sin A
1476 * y' = -(x - x0) * sin A + (y - y0) * cos A
1477 * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
1478 * l = sqrt( (x1-x0)^2 + (y1-y0)^2
1479 * Then determine inclusion by simple comparisons i.e.:
1480 * rectangle: x >= 0 && x < l && y >= 0 && y < h
1481 * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
1483 static int kp_match_area(const struct keypad_entry
*e
, int x
, int y
)
1485 double xp
, dx
= (e
->x1
- e
->x0
);
1486 double yp
, dy
= (e
->y1
- e
->y0
);
1487 double l
= sqrt(dx
*dx
+ dy
*dy
);
1490 if (l
> 1) { /* large enough */
1491 xp
= ((x
- e
->x0
)*dx
+ (y
- e
->y0
)*dy
)/l
;
1492 yp
= (-(x
- e
->x0
)*dy
+ (y
- e
->y0
)*dx
)/l
;
1493 if (e
->type
== KP_RECT
) {
1494 ret
= (xp
>= 0 && xp
< l
&& yp
>=0 && yp
< e
->h
);
1495 } else if (e
->type
== KP_CIRCLE
) {
1496 dx
= xp
*xp
/(l
*l
) + yp
*yp
/(e
->h
*e
->h
);
1501 ast_log(LOG_WARNING
, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
1502 ret
, e
->c
, x
, y
, e
->type
, e
->x0
, e
->y0
, e
->x1
, e
->y1
, e
->h
);
1507 struct _s_k
{ const char *s
; int k
; };
1508 static struct _s_k gui_key_map
[] = {
1509 {"FREEZE", KEY_FREEZE
},
1511 {"PICK_UP", KEY_PICK_UP
},
1512 {"PICKUP", KEY_PICK_UP
},
1513 {"HANG_UP", KEY_HANG_UP
},
1514 {"HANGUP", KEY_HANG_UP
},
1515 {"MUTE", KEY_MUTE
},
1516 {"FLASH", KEY_FLASH
},
1517 {"AUTOANSWER", KEY_AUTOANSWER
},
1518 {"SENDVIDEO", KEY_SENDVIDEO
},
1519 {"LOCALVIDEO", KEY_LOCALVIDEO
},
1520 {"REMOTEVIDEO", KEY_REMOTEVIDEO
},
1521 {"GUI_CLOSE", KEY_GUI_CLOSE
},
1522 {"MESSAGEBOARD", KEY_MESSAGEBOARD
},
1523 {"DIALEDBOARD", KEY_DIALEDBOARD
},
1524 {"EDITBOARD", KEY_EDITBOARD
},
1525 {"KEYPAD", KEY_KEYPAD
}, /* x0 y0 w h - active area of the keypad */
1526 {"MESSAGE", KEY_MESSAGE
}, /* x0 y0 w h - incoming messages */
1527 {"DIALED", KEY_DIALED
}, /* x0 y0 w h - dialed number */
1528 {"EDIT", KEY_EDIT
}, /* x0 y0 w h - edit user input */
1529 {"FONT", KEY_FONT
}, /* x0 yo w h rows cols - location and format of the font */
1532 static int gui_map_token(const char *s
)
1534 /* map the string into token to be returned */
1537 if (i
> 0 || s
[1] == '\0') /* numbers or single characters */
1538 return (i
> 9) ? i
: s
[0];
1539 for (p
= gui_key_map
; p
->s
; p
++) {
1540 if (!strcasecmp(p
->s
, s
))
1543 return KEY_NONE
; /* not found */
1546 /*! \brief read a keypad entry line in the format
1548 * token circle xc yc diameter
1549 * token circle xc yc x1 y1 h # ellipse, main diameter and height
1550 * token rect x0 y0 x1 y1 h # rectangle with main side and eight
1551 * token x0 y0 w h # horizontal rectangle (short format)
1552 * # this is used e.g. for message boards
1553 * token is the token to be returned, either a character or a symbol
1555 * Return 1 on success, 0 on error.
1557 static int keypad_cfg_read(struct gui_info
*gui
, const char *val
)
1559 struct keypad_entry e
;
1561 char s1
[16], s2
[16];
1562 int i
, ret
= 0; /* default, error */
1564 if (gui
== NULL
|| val
== NULL
)
1567 s1
[0] = s2
[0] = '\0';
1568 bzero(&e
, sizeof(e
));
1569 i
= sscanf(val
, "%14s %14s %d %d %d %d %d",
1570 s1
, s2
, &e
.x0
, &e
.y0
, &e
.x1
, &e
.y1
, &e
.h
);
1572 e
.c
= gui_map_token(s1
);
1573 if (e
.c
== KEY_NONE
)
1574 return 0; /* nothing found */
1578 case 1: /* only "reset" is allowed */
1579 if (e
.c
!= KEY_RESET
)
1585 if (e
.c
== KEY_KEYPAD
) /* active keypad area */
1587 else if (e
.c
== KEY_MESSAGE
)
1589 else if (e
.c
== KEY_DIALED
)
1591 else if (e
.c
== KEY_EDIT
)
1594 r
->x
= atoi(s2
); /* this becomes x0 */
1595 r
->y
= e
.x0
; /* this becomes y0 */
1596 r
->w
= e
.y0
; /* this becomes w */
1597 r
->h
= e
.x1
; /* this becomes h */
1600 if (strcasecmp(s2
, "circle")) /* invalid */
1602 /* token circle xc yc diameter */
1604 e
.y1
= e
.y0
; /* map radius in x1 y1 */
1605 e
.x1
= e
.x0
+ e
.h
; /* map radius in x1 y1 */
1606 e
.x0
= e
.x0
- e
.h
; /* map radius in x1 y1 */
1610 if (e
.c
== KEY_FONT
) { /* font - x0 y0 w h rows cols */
1611 ast_log(LOG_WARNING
, "font not supported yet\n");
1614 /* token circle|rect x0 y0 x1 y1 h */
1615 if (e
.x1
< e
.x0
|| e
.h
<= 0) {
1616 ast_log(LOG_WARNING
, "error in coordinates\n");
1620 if (!strcasecmp(s2
, "circle")) {
1621 /* for a circle we specify the diameter but store center and radii */
1623 e
.x0
= (e
.x1
+ e
.x0
) / 2;
1624 e
.y0
= (e
.y1
+ e
.y0
) / 2;
1626 } else if (!strcasecmp(s2
, "rect")) {
1632 // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
1635 if (gui
->kp_size
== 0) {
1636 gui
->kp
= ast_calloc(10, sizeof(e
));
1637 if (gui
->kp
== NULL
) {
1638 ast_log(LOG_WARNING
, "cannot allocate kp");
1643 if (gui
->kp_size
== gui
->kp_used
) { /* must allocate */
1644 struct keypad_entry
*a
= ast_realloc(gui
->kp
, sizeof(e
)*(gui
->kp_size
+10));
1646 ast_log(LOG_WARNING
, "cannot reallocate kp");
1652 if (gui
->kp_size
== gui
->kp_used
)
1654 gui
->kp
[gui
->kp_used
++] = e
;
1655 // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
1658 #endif /* HAVE_SDL */