1 /*****************************************************************************
2 * puzzle.c : Puzzle game
3 *****************************************************************************
4 * Copyright (C) 2005-2006 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_plugin.h>
38 #include "filter_common.h"
39 #include "vlc_image.h"
40 #include "vlc_input.h"
41 #include "vlc_playlist.h"
43 /*****************************************************************************
45 *****************************************************************************/
46 static int Create ( vlc_object_t * );
47 static void Destroy ( vlc_object_t * );
49 static int Init ( vout_thread_t * );
50 static void End ( vout_thread_t * );
51 static void Render ( vout_thread_t *, picture_t * );
53 static int SendEvents ( vlc_object_t *, char const *,
54 vlc_value_t, vlc_value_t, void * );
55 static int MouseEvent ( vlc_object_t *, char const *,
56 vlc_value_t, vlc_value_t, void * );
58 static int PuzzleCallback( vlc_object_t *, char const *,
59 vlc_value_t, vlc_value_t, void * );
61 /*****************************************************************************
63 *****************************************************************************/
65 #define ROWS_TEXT N_("Number of puzzle rows")
66 #define ROWS_LONGTEXT N_("Number of puzzle rows")
67 #define COLS_TEXT N_("Number of puzzle columns")
68 #define COLS_LONGTEXT N_("Number of puzzle columns")
69 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
70 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
72 #define CFG_PREFIX "puzzle-"
75 set_description( _("Puzzle interactive game video filter") );
76 set_shortname( _( "Puzzle" ));
77 set_capability( "video filter", 0 );
78 set_category( CAT_VIDEO );
79 set_subcategory( SUBCAT_VIDEO_VFILTER );
81 add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
82 ROWS_TEXT, ROWS_LONGTEXT, false );
83 add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
84 COLS_TEXT, COLS_LONGTEXT, false );
85 add_bool( CFG_PREFIX "black-slot", 0, NULL,
86 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false );
88 set_callbacks( Create, Destroy );
91 static const char *ppsz_filter_options[] = {
92 "rows", "cols", "black-slot", NULL
95 /*****************************************************************************
96 * vout_sys_t: Magnify video output method descriptor
97 *****************************************************************************/
100 vout_thread_t *p_vout;
102 image_handler_t *p_image;
113 /*****************************************************************************
114 * Control: control facility for the vout (forwards to child vout)
115 *****************************************************************************/
116 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
118 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
121 /*****************************************************************************
123 *****************************************************************************/
124 static bool finished( vout_sys_t *p_sys )
127 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
129 if( i != p_sys->pi_order[i] ) return false;
133 static bool is_valid( vout_sys_t *p_sys )
136 if( p_sys->b_blackslot == false ) return true;
137 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
139 if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
141 d += i / p_sys->i_cols + 1;
144 for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
146 if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
148 if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
151 if( d%2!=0 ) return false;
154 static void shuffle( vout_sys_t *p_sys )
157 free( p_sys->pi_order );
158 p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
161 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
163 p_sys->pi_order[i] = -1;
166 for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
168 i = rand()%( p_sys->i_cols * p_sys->i_rows );
169 if( p_sys->pi_order[i] == -1 )
171 p_sys->pi_order[i] = c;
175 p_sys->b_finished = finished( p_sys );
176 } while( p_sys->b_finished == true
177 || is_valid( p_sys ) == false );
179 if( p_sys->b_blackslot == true )
181 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
183 if( p_sys->pi_order[i] ==
184 p_sys->i_cols * p_sys->i_rows - 1 )
186 p_sys->i_selected = i;
193 p_sys->i_selected = -1;
197 /*****************************************************************************
198 * Create: allocates Magnify video thread output method
199 *****************************************************************************/
200 static int Create( vlc_object_t *p_this )
202 vout_thread_t *p_vout = (vout_thread_t *)p_this;
204 /* Allocate structure */
205 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
206 if( p_vout->p_sys == NULL )
208 msg_Err( p_vout, "out of memory" );
212 p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
214 config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
217 p_vout->p_sys->i_rows =
218 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
219 p_vout->p_sys->i_cols =
220 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
221 p_vout->p_sys->b_blackslot =
222 var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
223 var_AddCallback( p_vout, CFG_PREFIX "rows",
224 PuzzleCallback, p_vout->p_sys );
225 var_AddCallback( p_vout, CFG_PREFIX "cols",
226 PuzzleCallback, p_vout->p_sys );
227 var_AddCallback( p_vout, CFG_PREFIX "black-slot",
228 PuzzleCallback, p_vout->p_sys );
230 p_vout->p_sys->pi_order = NULL;
231 shuffle( p_vout->p_sys );
233 p_vout->pf_init = Init;
234 p_vout->pf_end = End;
235 p_vout->pf_manage = NULL;
236 p_vout->pf_render = Render;
237 p_vout->pf_display = NULL;
238 p_vout->pf_control = Control;
243 /*****************************************************************************
244 * Init: initialize Magnify video thread output method
245 *****************************************************************************/
246 static int Init( vout_thread_t *p_vout )
251 memset( &fmt, 0, sizeof( video_format_t ) );
253 I_OUTPUTPICTURES = 0;
255 /* Initialize the output structure */
256 p_vout->output.i_chroma = p_vout->render.i_chroma;
257 p_vout->output.i_width = p_vout->render.i_width;
258 p_vout->output.i_height = p_vout->render.i_height;
259 p_vout->output.i_aspect = p_vout->render.i_aspect;
261 p_vout->fmt_out = p_vout->fmt_in;
262 fmt = p_vout->fmt_out;
264 /* Try to open the real video output */
265 msg_Dbg( p_vout, "spawning the real video output" );
267 p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
269 /* Everything failed */
270 if( p_vout->p_sys->p_vout == NULL )
272 msg_Err( p_vout, "cannot open vout, aborting" );
276 var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
277 var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
278 var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
281 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
282 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
283 ADD_PARENT_CALLBACKS( SendEventsToChild );
288 /*****************************************************************************
289 * End: terminate Magnify video thread output method
290 *****************************************************************************/
291 static void End( vout_thread_t *p_vout )
295 /* Free the fake output buffers we allocated */
296 for( i_index = I_OUTPUTPICTURES ; i_index ; )
299 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
302 var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
303 var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
304 var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
307 #define SHUFFLE_WIDTH 81
308 #define SHUFFLE_HEIGHT 13
309 static const char *shuffle_button[] =
311 ".................................................................................",
312 ".............. ............................ ........ ...... ...............",
313 ".............. ........................... ......... ........ ...............",
314 ".............. ........................... ......... ........ ...............",
315 ".. ....... . ....... .... ...... ...... ...... ........ ...",
316 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
317 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
318 ". ....... .... ...... .... ....... ......... ........ ...... .",
319 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
320 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
321 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
322 ".. ....... .... ....... . ....... ......... ........ ........ ..",
323 "................................................................................."};
326 /*****************************************************************************
327 * Destroy: destroy Magnify video thread output method
328 *****************************************************************************/
329 static void Destroy( vlc_object_t *p_this )
331 vout_thread_t *p_vout = (vout_thread_t *)p_this;
333 if( p_vout->p_sys->p_vout )
335 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
336 vlc_object_detach( p_vout->p_sys->p_vout );
337 vout_Destroy( p_vout->p_sys->p_vout );
340 image_HandlerDelete( p_vout->p_sys->p_image );
341 free( p_vout->p_sys->pi_order );
343 DEL_PARENT_CALLBACKS( SendEventsToChild );
345 free( p_vout->p_sys );
348 /*****************************************************************************
349 * Render: displays previously rendered output
350 *****************************************************************************/
351 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
355 //video_format_t fmt_out;
356 // memset( &fmt_out, 0, sizeof(video_format_t) );
357 //picture_t *p_converted;
361 int i_rows = p_vout->p_sys->i_rows;
362 int i_cols = p_vout->p_sys->i_cols;
364 /* This is a new frame. Get a structure from the video_output. */
365 while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
368 if( p_vout->b_die || p_vout->b_error )
372 msleep( VOUT_OUTMEM_SLEEP );
375 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
377 for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
379 plane_t *p_in = p_pic->p+i_plane;
380 plane_t *p_out = p_outpic->p+i_plane;
381 int i_pitch = p_in->i_pitch;
384 for( i = 0; i < i_cols * i_rows; i++ )
386 int i_col = i % i_cols;
387 int i_row = i / i_cols;
388 int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
389 int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
390 int i_last_row = i_row + 1;
391 i_orow *= p_in->i_lines / i_rows;
392 i_row *= p_in->i_lines / i_rows;
393 i_last_row *= p_in->i_lines / i_rows;
395 if( p_vout->p_sys->b_blackslot == true
396 && p_vout->p_sys->b_finished == false
397 && i == p_vout->p_sys->i_selected )
399 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
400 for( ; i_row < i_last_row; i_row++, i_orow++ )
402 vlc_memset( p_out->p_pixels + i_row * i_pitch
403 + i_col * i_pitch / i_cols,
404 color, i_pitch / i_cols );
409 for( ; i_row < i_last_row; i_row++, i_orow++ )
411 vlc_memcpy( p_out->p_pixels + i_row * i_pitch
412 + i_col * i_pitch / i_cols,
413 p_in->p_pixels + i_orow * i_pitch
414 + i_ocol * i_pitch / i_cols,
421 if( p_vout->p_sys->i_selected != -1
422 && p_vout->p_sys->b_blackslot == false )
424 plane_t *p_in = p_pic->p+Y_PLANE;
425 plane_t *p_out = p_outpic->p+Y_PLANE;
426 int i_pitch = p_in->i_pitch;
427 int i_col = p_vout->p_sys->i_selected % i_cols;
428 int i_row = p_vout->p_sys->i_selected / i_cols;
429 int i_last_row = i_row + 1;
430 i_row *= p_in->i_lines / i_rows;
431 i_last_row *= p_in->i_lines / i_rows;
432 vlc_memset( p_out->p_pixels + i_row * i_pitch
433 + i_col * i_pitch / i_cols,
434 0xff, i_pitch / i_cols );
435 for( ; i_row < i_last_row; i_row++ )
437 p_out->p_pixels[ i_row * i_pitch
438 + i_col * i_pitch / i_cols ] = 0xff;
439 p_out->p_pixels[ i_row * i_pitch
440 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
443 vlc_memset( p_out->p_pixels + i_row * i_pitch
444 + i_col * i_pitch / i_cols,
445 0xff, i_pitch / i_cols );
448 if( p_vout->p_sys->b_finished == true )
451 plane_t *p_out = p_outpic->p+Y_PLANE;
452 int i_pitch = p_out->i_pitch;
453 for( i = 0; i < SHUFFLE_HEIGHT; i++ )
455 for( j = 0; j < SHUFFLE_WIDTH; j++ )
457 if( shuffle_button[i][j] == '.' )
458 p_out->p_pixels[ i * i_pitch + j ] = 0xff;
463 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
466 /*****************************************************************************
467 * SendEvents: forward mouse and keyboard events to the parent p_vout
468 *****************************************************************************/
469 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
470 vlc_value_t oldval, vlc_value_t newval, void *p_data )
472 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
474 var_Set( (vlc_object_t *)p_data, psz_var, newval );
479 /*****************************************************************************
480 * SendEventsToChild: forward events to the child/children vout
481 *****************************************************************************/
482 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
483 vlc_value_t oldval, vlc_value_t newval, void *p_data )
485 VLC_UNUSED(p_data); VLC_UNUSED(oldval);
486 vout_thread_t *p_vout = (vout_thread_t *)p_this;
487 var_Set( p_vout->p_sys->p_vout, psz_var, newval );
491 /*****************************************************************************
492 * MouseEvent: callback for mouse events
493 *****************************************************************************/
494 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
495 vlc_value_t oldval, vlc_value_t newval, void *p_data )
497 VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(newval);
498 vout_thread_t *p_vout = (vout_thread_t*)p_data;
503 #define MOUSE_CLICKED 2
504 #define MOUSE_MOVE_X 4
505 #define MOUSE_MOVE_Y 8
506 #define MOUSE_MOVE 12
509 int v_h = p_vout->output.i_height;
510 int v_w = p_vout->output.i_width;
513 if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
514 if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
515 if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
517 i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
518 if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
519 i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
520 i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
522 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
525 if( mouse & MOUSE_CLICKED )
527 i_pos = p_vout->p_sys->i_cols * ( ( p_vout->p_sys->i_rows * i_y ) / v_h ) + (p_vout->p_sys->i_cols * i_x ) / v_w;
528 if( p_vout->p_sys->b_finished == true
529 && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
531 shuffle( p_vout->p_sys );
533 else if( p_vout->p_sys->i_selected == -1 )
535 p_vout->p_sys->i_selected = i_pos;
537 else if( p_vout->p_sys->i_selected == i_pos
538 && p_vout->p_sys->b_blackslot == false )
540 p_vout->p_sys->i_selected = -1;
542 else if( ( p_vout->p_sys->i_selected == i_pos + 1
543 && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
544 || ( p_vout->p_sys->i_selected == i_pos - 1
545 && i_pos % p_vout->p_sys->i_cols != 0 )
546 || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
547 || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
549 int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
550 p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
551 p_vout->p_sys->pi_order[ i_pos ];
552 p_vout->p_sys->pi_order[ i_pos ] = a;
553 if( p_vout->p_sys->b_blackslot == true )
554 p_vout->p_sys->i_selected = i_pos;
556 p_vout->p_sys->i_selected = -1;
558 p_vout->p_sys->b_finished = finished( p_vout->p_sys );
564 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
565 vlc_value_t oldval, vlc_value_t newval,
568 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
569 vout_sys_t *p_sys = (vout_sys_t *)p_data;
570 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
572 p_sys->i_rows = __MAX( 1, newval.i_int );
574 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
576 p_sys->i_cols = __MAX( 1, newval.i_int );
578 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
580 p_sys->b_blackslot = newval.b_bool;