2 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
4 * This file is part of libdvdnav, a DVD navigation library.
6 * libdvdnav is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * libdvdnav is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <dvdread/nav_types.h>
33 #include "dvdnav/dvdnav.h"
35 #include "vm/decoder.h"
38 #include "dvdnav_internal.h"
41 #define BUTTON_TESTING
46 #include "nav_print.h"
48 static void print_time(dvd_time_t
*dtime
) {
51 assert((dtime
->hour
>>4) < 0xa && (dtime
->hour
&0xf) < 0xa);
52 assert((dtime
->minute
>>4) < 0x7 && (dtime
->minute
&0xf) < 0xa);
53 assert((dtime
->second
>>4) < 0x7 && (dtime
->second
&0xf) < 0xa);
54 assert((dtime
->frame_u
&0xf) < 0xa);
56 fprintf(MSG_OUT
,"%02x:%02x:%02x.%02x",
60 dtime
->frame_u
& 0x3f);
61 switch((dtime
->frame_u
& 0xc0) >> 6) {
69 rate
= "(please send a bug report)";
72 fprintf(MSG_OUT
," @ %s fps", rate
);
75 static void nav_print_PCI_GI(pci_gi_t
*pci_gi
) {
78 fprintf(MSG_OUT
,"libdvdnav: pci_gi:\n");
79 fprintf(MSG_OUT
,"libdvdnav: nv_pck_lbn 0x%08x\n", pci_gi
->nv_pck_lbn
);
80 fprintf(MSG_OUT
,"libdvdnav: vobu_cat 0x%04x\n", pci_gi
->vobu_cat
);
81 fprintf(MSG_OUT
,"libdvdnav: vobu_uop_ctl 0x%08x\n", *(uint32_t*)&pci_gi
->vobu_uop_ctl
);
82 fprintf(MSG_OUT
,"libdvdnav: vobu_s_ptm 0x%08x\n", pci_gi
->vobu_s_ptm
);
83 fprintf(MSG_OUT
,"libdvdnav: vobu_e_ptm 0x%08x\n", pci_gi
->vobu_e_ptm
);
84 fprintf(MSG_OUT
,"libdvdnav: vobu_se_e_ptm 0x%08x\n", pci_gi
->vobu_se_e_ptm
);
85 fprintf(MSG_OUT
,"libdvdnav: e_eltm ");
86 print_time(&pci_gi
->e_eltm
);
87 fprintf(MSG_OUT
,"\n");
89 fprintf(MSG_OUT
,"libdvdnav: vobu_isrc \"");
90 for(i
= 0; i
< 32; i
++) {
91 char c
= pci_gi
->vobu_isrc
[i
];
92 if((c
>= ' ') && (c
<= '~'))
93 fprintf(MSG_OUT
,"%c", c
);
97 fprintf(MSG_OUT
,"\"\n");
100 static void nav_print_NSML_AGLI(nsml_agli_t
*nsml_agli
) {
103 for(i
= 0; i
< 9; i
++)
104 j
|= nsml_agli
->nsml_agl_dsta
[i
];
108 fprintf(MSG_OUT
,"libdvdnav: nsml_agli:\n");
109 for(i
= 0; i
< 9; i
++)
110 if(nsml_agli
->nsml_agl_dsta
[i
])
111 fprintf(MSG_OUT
,"libdvdnav: nsml_agl_c%d_dsta 0x%08x\n", i
+ 1,
112 nsml_agli
->nsml_agl_dsta
[i
]);
115 static void nav_print_HL_GI(hl_gi_t
*hl_gi
, int32_t *btngr_ns
, int32_t *btn_ns
) {
117 if((hl_gi
->hli_ss
& 0x03) == 0)
120 fprintf(MSG_OUT
,"libdvdnav: hl_gi:\n");
121 fprintf(MSG_OUT
,"libdvdnav: hli_ss 0x%01x\n", hl_gi
->hli_ss
& 0x03);
122 fprintf(MSG_OUT
,"libdvdnav: hli_s_ptm 0x%08x\n", hl_gi
->hli_s_ptm
);
123 fprintf(MSG_OUT
,"libdvdnav: hli_e_ptm 0x%08x\n", hl_gi
->hli_e_ptm
);
124 fprintf(MSG_OUT
,"libdvdnav: btn_se_e_ptm 0x%08x\n", hl_gi
->btn_se_e_ptm
);
126 *btngr_ns
= hl_gi
->btngr_ns
;
127 fprintf(MSG_OUT
,"libdvdnav: btngr_ns %d\n", hl_gi
->btngr_ns
);
128 fprintf(MSG_OUT
,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 1, hl_gi
->btngr1_dsp_ty
);
129 fprintf(MSG_OUT
,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 2, hl_gi
->btngr2_dsp_ty
);
130 fprintf(MSG_OUT
,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 3, hl_gi
->btngr3_dsp_ty
);
132 fprintf(MSG_OUT
,"libdvdnav: btn_ofn %d\n", hl_gi
->btn_ofn
);
133 *btn_ns
= hl_gi
->btn_ns
;
134 fprintf(MSG_OUT
,"libdvdnav: btn_ns %d\n", hl_gi
->btn_ns
);
135 fprintf(MSG_OUT
,"libdvdnav: nsl_btn_ns %d\n", hl_gi
->nsl_btn_ns
);
136 fprintf(MSG_OUT
,"libdvdnav: fosl_btnn %d\n", hl_gi
->fosl_btnn
);
137 fprintf(MSG_OUT
,"libdvdnav: foac_btnn %d\n", hl_gi
->foac_btnn
);
140 static void nav_print_BTN_COLIT(btn_colit_t
*btn_colit
) {
144 for(i
= 0; i
< 6; i
++)
145 j
|= btn_colit
->btn_coli
[i
/2][i
&1];
149 fprintf(MSG_OUT
,"libdvdnav: btn_colit:\n");
150 for(i
= 0; i
< 3; i
++)
151 for(j
= 0; j
< 2; j
++)
152 fprintf(MSG_OUT
,"libdvdnav: btn_cqoli %d %s_coli: %08x\n",
153 i
, (j
== 0) ? "sl" : "ac",
154 btn_colit
->btn_coli
[i
][j
]);
157 static void nav_print_BTNIT(btni_t
*btni_table
, int32_t btngr_ns
, int32_t btn_ns
) {
160 fprintf(MSG_OUT
,"libdvdnav: btnit:\n");
161 fprintf(MSG_OUT
,"libdvdnav: btngr_ns: %i\n", btngr_ns
);
162 fprintf(MSG_OUT
,"libdvdnav: btn_ns: %i\n", btn_ns
);
167 for(i
= 0; i
< btngr_ns
; i
++) {
168 for(j
= 0; j
< (36 / btngr_ns
); j
++) {
170 btni_t
*btni
= &btni_table
[(36 / btngr_ns
) * i
+ j
];
172 fprintf(MSG_OUT
,"libdvdnav: group %d btni %d: ", i
+1, j
+1);
173 fprintf(MSG_OUT
,"btn_coln %d, auto_action_mode %d\n",
174 btni
->btn_coln
, btni
->auto_action_mode
);
175 fprintf(MSG_OUT
,"libdvdnav: coords (%d, %d) .. (%d, %d)\n",
176 btni
->x_start
, btni
->y_start
, btni
->x_end
, btni
->y_end
);
178 fprintf(MSG_OUT
,"libdvdnav: up %d, ", btni
->up
);
179 fprintf(MSG_OUT
,"down %d, ", btni
->down
);
180 fprintf(MSG_OUT
,"left %d, ", btni
->left
);
181 fprintf(MSG_OUT
,"right %d\n", btni
->right
);
182 for(k
= 0; k
< 8; k
++) {
183 fprintf(MSG_OUT
, "libdvdnav: %02x ", btni
->cmd
.bytes
[k
]);
185 fprintf(MSG_OUT
, "| ");
187 vm_print_mnemonic(&btni
->cmd
);
189 fprintf(MSG_OUT
, "\n");
195 static void nav_print_HLI(hli_t
*hli
) {
196 int32_t btngr_ns
= 0, btn_ns
= 0;
198 fprintf(MSG_OUT
,"libdvdnav: hli:\n");
199 nav_print_HL_GI(&hli
->hl_gi
, & btngr_ns
, & btn_ns
);
200 nav_print_BTN_COLIT(&hli
->btn_colit
);
201 nav_print_BTNIT(hli
->btnit
, btngr_ns
, btn_ns
);
204 void nav_print_PCI(pci_t
*pci
) {
205 fprintf(MSG_OUT
,"libdvdnav: pci packet:\n");
206 nav_print_PCI_GI(&pci
->pci_gi
);
207 nav_print_NSML_AGLI(&pci
->nsml_agli
);
208 nav_print_HLI(&pci
->hli
);
214 /* Highlighting API calls */
216 dvdnav_status_t
dvdnav_get_current_highlight(dvdnav_t
*this, int32_t *button
) {
217 /* Simply return the appropriate value based on the SPRM */
218 if(((*button
) = this->position_current
.button
) == -1)
219 (*button
) = this->vm
->state
.HL_BTNN_REG
>> 10;
221 return DVDNAV_STATUS_OK
;
224 static btni_t
*get_current_button(dvdnav_t
*this, pci_t
*pci
) {
227 if(!pci
->hli
.hl_gi
.hli_ss
) {
228 printerr("Not in a menu.");
231 if(this->last_cmd_nav_lbn
== pci
->pci_gi
.nv_pck_lbn
) {
232 printerr("This NAV has already been left.");
236 button
= this->vm
->state
.HL_BTNN_REG
>> 10;
237 #ifdef BUTTON_TESTING
241 return &(pci
->hli
.btnit
[button
-1]);
244 static dvdnav_status_t
button_auto_action(dvdnav_t
*this, pci_t
*pci
) {
245 if (get_current_button(this, pci
)->auto_action_mode
)
246 return dvdnav_button_activate(this, pci
);
247 return DVDNAV_STATUS_OK
;
250 dvdnav_status_t
dvdnav_upper_button_select(dvdnav_t
*this, pci_t
*pci
) {
253 if(!(button_ptr
= get_current_button(this, pci
)))
254 return DVDNAV_STATUS_ERR
;
256 dvdnav_button_select(this, pci
, button_ptr
->up
);
257 return button_auto_action(this, pci
);
260 dvdnav_status_t
dvdnav_lower_button_select(dvdnav_t
*this, pci_t
*pci
) {
263 if(!(button_ptr
= get_current_button(this, pci
)))
264 return DVDNAV_STATUS_ERR
;
266 dvdnav_button_select(this, pci
, button_ptr
->down
);
267 return button_auto_action(this, pci
);
270 dvdnav_status_t
dvdnav_right_button_select(dvdnav_t
*this, pci_t
*pci
) {
273 if(!(button_ptr
= get_current_button(this, pci
)))
274 return DVDNAV_STATUS_ERR
;
276 dvdnav_button_select(this, pci
, button_ptr
->right
);
277 return button_auto_action(this, pci
);
280 dvdnav_status_t
dvdnav_left_button_select(dvdnav_t
*this, pci_t
*pci
) {
283 if(!(button_ptr
= get_current_button(this, pci
)))
284 return DVDNAV_STATUS_ERR
;
286 dvdnav_button_select(this, pci
, button_ptr
->left
);
287 return button_auto_action(this, pci
);
290 dvdnav_status_t
dvdnav_get_highlight_area(pci_t
*nav_pci
, int32_t button
, int32_t mode
,
291 dvdnav_highlight_area_t
*highlight
) {
294 #ifdef BUTTON_TESTING
295 fprintf(MSG_OUT
, "libdvdnav: Button get_highlight_area %i\n", button
);
298 if(!nav_pci
->hli
.hl_gi
.hli_ss
)
299 return DVDNAV_STATUS_ERR
;
300 if((button
<= 0) || (button
> nav_pci
->hli
.hl_gi
.btn_ns
))
301 return DVDNAV_STATUS_ERR
;
304 button_ptr
= &nav_pci
->hli
.btnit
[button
-1];
306 highlight
->sx
= button_ptr
->x_start
;
307 highlight
->sy
= button_ptr
->y_start
;
308 highlight
->ex
= button_ptr
->x_end
;
309 highlight
->ey
= button_ptr
->y_end
;
310 if(button_ptr
->btn_coln
!= 0) {
311 highlight
->palette
= nav_pci
->hli
.btn_colit
.btn_coli
[button_ptr
->btn_coln
-1][mode
];
313 highlight
->palette
= 0;
315 highlight
->pts
= nav_pci
->hli
.hl_gi
.hli_s_ptm
;
316 highlight
->buttonN
= button
;
317 #ifdef BUTTON_TESTING
318 fprintf(MSG_OUT
, "libdvdnav: highlight: Highlight area is (%u,%u)-(%u,%u), display = %i, button = %u\n",
319 button_ptr
->x_start
, button_ptr
->y_start
,
320 button_ptr
->x_end
, button_ptr
->y_end
,
325 return DVDNAV_STATUS_OK
;
328 dvdnav_status_t
dvdnav_button_activate(dvdnav_t
*this, pci_t
*pci
) {
330 btni_t
*button_ptr
= NULL
;
332 if(!pci
->hli
.hl_gi
.hli_ss
) {
333 printerr("Not in a menu.");
334 return DVDNAV_STATUS_ERR
;
336 if(this->last_cmd_nav_lbn
== pci
->pci_gi
.nv_pck_lbn
) {
337 printerr("This NAV has already been left.");
338 return DVDNAV_STATUS_ERR
;
340 pthread_mutex_lock(&this->vm_lock
);
342 button
= this->vm
->state
.HL_BTNN_REG
>> 10;
344 if((button
<= 0) || (button
> pci
->hli
.hl_gi
.btn_ns
)) {
345 /* Special code to handle still menus with no buttons.
346 * The navigation is expected to report to the application that a STILL is
347 * underway. In turn, the application is supposed to report to the user
348 * that the playback is paused. The user is then expected to undo the pause,
349 * ie: hit play. At that point, the navigation should release the still and
350 * go to the next Cell.
351 * Explanation by Mathieu Lacage <mathieu_lacage@realmagic.fr>
352 * Code added by jcdutton.
354 if (this->position_current
.still
!= 0) {
355 /* In still, but no buttons. */
356 vm_get_next_cell(this->vm
);
357 this->position_current
.still
= 0;
359 this->last_cmd_nav_lbn
= pci
->pci_gi
.nv_pck_lbn
;
360 pthread_mutex_unlock(&this->vm_lock
);
361 /* clear error message */
363 return DVDNAV_STATUS_OK
;
365 pthread_mutex_unlock(&this->vm_lock
);
366 return DVDNAV_STATUS_ERR
;
369 button_ptr
= get_current_button(this, pci
);
370 /* Finally, make the VM execute the appropriate code and probably
372 #ifdef BUTTON_TESTING
373 fprintf(MSG_OUT
, "libdvdnav: Evaluating Button Activation commands.\n");
375 if(vm_exec_cmd(this->vm
, &(button_ptr
->cmd
)) == 1) {
376 /* Command caused a jump */
377 this->vm
->hop_channel
++;
378 this->position_current
.still
= 0;
379 this->last_cmd_nav_lbn
= pci
->pci_gi
.nv_pck_lbn
;
382 pthread_mutex_unlock(&this->vm_lock
);
383 return DVDNAV_STATUS_OK
;
386 dvdnav_status_t
dvdnav_button_activate_cmd(dvdnav_t
*this, int32_t button
, vm_cmd_t
*cmd
)
388 pthread_mutex_lock(&this->vm_lock
);
389 /* make the VM execute the appropriate code and probably
391 #ifdef BUTTON_TESTING
392 fprintf(MSG_OUT
, "libdvdnav: dvdnav_button_activate_cmd: Evaluating Button Activation commands.\n");
395 this->vm
->state
.HL_BTNN_REG
= (button
<< 10);
396 if(vm_exec_cmd(this->vm
, cmd
) == 1) {
397 /* Command caused a jump */
398 this->vm
->hop_channel
++;
401 /* Always remove still, because some still menus have no buttons. */
402 this->position_current
.still
= 0;
404 pthread_mutex_unlock(&this->vm_lock
);
405 return DVDNAV_STATUS_OK
;
408 dvdnav_status_t
dvdnav_button_select(dvdnav_t
*this, pci_t
*pci
, int32_t button
) {
409 if(!pci
->hli
.hl_gi
.hli_ss
) {
410 printerr("Not in a menu.");
411 return DVDNAV_STATUS_ERR
;
413 if(this->last_cmd_nav_lbn
== pci
->pci_gi
.nv_pck_lbn
) {
414 printerr("This NAV has already been left.");
415 return DVDNAV_STATUS_ERR
;
418 #ifdef BUTTON_TESTING
419 fprintf(MSG_OUT
, "libdvdnav: Button select %i\n", button
);
422 if((button
<= 0) || (button
> pci
->hli
.hl_gi
.btn_ns
)) {
423 printerr("Button does not exist.");
424 return DVDNAV_STATUS_ERR
;
427 this->vm
->state
.HL_BTNN_REG
= (button
<< 10);
428 this->position_current
.button
= -1; /* Force Highligh change */
430 return DVDNAV_STATUS_OK
;
433 dvdnav_status_t
dvdnav_button_select_and_activate(dvdnav_t
*this, pci_t
*pci
,
435 /* A trivial function */
436 if(dvdnav_button_select(this, pci
, button
) != DVDNAV_STATUS_ERR
)
437 return dvdnav_button_activate(this, pci
);
438 return DVDNAV_STATUS_ERR
;
441 dvdnav_status_t
dvdnav_mouse_select(dvdnav_t
*this, pci_t
*pci
, int32_t x
, int32_t y
) {
442 int32_t button
, cur_button
;
446 if(!pci
->hli
.hl_gi
.hli_ss
) {
447 printerr("Not in a menu.");
448 return DVDNAV_STATUS_ERR
;
450 if(this->last_cmd_nav_lbn
== pci
->pci_gi
.nv_pck_lbn
) {
451 printerr("This NAV has already been left.");
452 return DVDNAV_STATUS_ERR
;
455 cur_button
= this->vm
->state
.HL_BTNN_REG
>> 10;
458 dist
= 0x08000000; /* >> than (720*720)+(567*567); */
460 /* Loop through all buttons */
461 for(button
= 1; button
<= pci
->hli
.hl_gi
.btn_ns
; button
++) {
462 btni_t
*button_ptr
= &(pci
->hli
.btnit
[button
-1]);
464 if((x
>= button_ptr
->x_start
) && (x
<= button_ptr
->x_end
) &&
465 (y
>= button_ptr
->y_start
) && (y
<= button_ptr
->y_end
)) {
466 mx
= (button_ptr
->x_start
+ button_ptr
->x_end
)/2;
467 my
= (button_ptr
->y_start
+ button_ptr
->y_end
)/2;
470 d
= (dx
*dx
) + (dy
*dy
);
471 /* If the mouse is within the button and the mouse is closer
472 * to the center of this button then it is the best choice. */
479 /* As an efficiency measure, only re-select the button
480 * if it is different to the previously selected one. */
481 if (best
!= 0 && best
!= cur_button
)
482 dvdnav_button_select(this, pci
, best
);
484 /* return DVDNAV_STATUS_OK only if we actually found a matching button */
485 return best
? DVDNAV_STATUS_OK
: DVDNAV_STATUS_ERR
;
488 dvdnav_status_t
dvdnav_mouse_activate(dvdnav_t
*this, pci_t
*pci
, int32_t x
, int32_t y
) {
489 /* A trivial function */
490 if(dvdnav_mouse_select(this, pci
, x
,y
) != DVDNAV_STATUS_ERR
)
491 return dvdnav_button_activate(this, pci
);
492 return DVDNAV_STATUS_ERR
;