1 /*
2  * This file is part of MPlayer.
3  *
4  * MPlayer is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * MPlayer is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 /* main window */
20 
21 #include <math.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <string.h>
27 
28 #include "ui.h"
29 #include "gui/app/app.h"
30 #include "gui/app/gui.h"
31 #include "gui/interface.h"
32 #include "gui/skin/font.h"
33 #include "gui/skin/skin.h"
34 #include "gui/util/list.h"
35 #include "gui/util/mem.h"
36 #include "gui/util/misc.h"
37 #include "gui/util/string.h"
38 #include "gui/wm/ws.h"
39 #include "gui/wm/wsxdnd.h"
40 
41 #include "help_mp.h"
42 #include "mp_msg.h"
43 #include "libvo/x11_common.h"
44 #include "libvo/fastmemcpy.h"
45 #include "libvo/wskeys.h"
46 
47 #include "stream/stream.h"
48 #include "stream/url.h"
49 
50 #include "libmpdemux/demuxer.h"
51 #include "libmpdemux/stheader.h"
52 #include "codec-cfg.h"
53 #include "m_option.h"
54 #include "mp_core.h"
55 #include "libavutil/avstring.h"
56 #include "libavutil/common.h"
57 
58 #include "actions.h"
59 
60 static unsigned char * mainDrawBuffer;
61 int             uiMainRender = True;
62 
63 int             mainVisible = True;
64 
65 int             boxMoved = False;
66 int             sx = 0,sy = 0;
67 int             i,pot = 0;
68 
69 #include "render.h"
70 
uiMainDraw(void)71 static void uiMainDraw( void )
72 {
73 
74  if ( guiApp.mainWindow.State == wsWindowClosed ) mplayer( MPLAYER_EXIT_GUI, EXIT_QUIT, 0 );
75 
76  if ( guiApp.mainWindow.Visible == wsWindowNotVisible ||
77       !mainVisible ) return;
78 //      !guiApp.mainWindow.Mapped ) return;
79 
80  if ( uiMainRender && guiApp.mainWindow.State == wsWindowExpose )
81   {
82    btnModify( evSetMoviePosition,guiInfo.Position );
83    btnModify( evSetVolume,guiInfo.Volume );
84    btnModify( evSetBalance,guiInfo.Balance );
85 
86    fast_memcpy( mainDrawBuffer,guiApp.main.Bitmap.Image,guiApp.main.Bitmap.ImageSize );
87    RenderAll( &guiApp.mainWindow,guiApp.mainItems,guiApp.IndexOfMainItems,mainDrawBuffer );
88    uiMainRender=False;
89   }
90  // NOTE TO MYSELF: probably not, if uiMainRender is False
91  wsImageDraw( &guiApp.mainWindow );
92 // XFlush( wsDisplay );
93 }
94 
uiMainMouse(int Button,int X,int Y,int RX,int RY)95 static void uiMainMouse( int Button,int X,int Y,int RX,int RY )
96 {
97  static int     itemtype = 0;
98         int     i;
99         guiItem * item = NULL;
100  static double  prev_point;
101         double  point;
102         float   value = 0.0f;
103 
104  static int     SelectedItem = -1;
105         int     currentselected = -1;
106  static int     endstop;
107 
108  for ( i=0;i <= guiApp.IndexOfMainItems;i++ )
109   if ( ( guiApp.mainItems[i].pressed != btnDisabled )&&
110        ( isInside( X,Y,guiApp.mainItems[i].x,guiApp.mainItems[i].y,guiApp.mainItems[i].x+guiApp.mainItems[i].width,guiApp.mainItems[i].y+guiApp.mainItems[i].height ) ) )
111    { currentselected=i; break; }
112 
113  switch ( Button )
114   {
115    case wsPMMouseButton:
116           gtkShow( ivHidePopUpMenu,NULL );
117           uiMenuShow( RX,RY );
118           itemtype=itPRMButton;
119           break;
120    case wsRMMouseButton:
121           uiMenuHide( RX,RY,0 );
122           break;
123 
124    case wsPLMouseButton:
125           gtkShow( ivHidePopUpMenu,NULL );
126           sx=X; sy=Y; boxMoved=True; itemtype=itPLMButton;
127           SelectedItem=currentselected;
128           if ( SelectedItem == -1 ) break;
129           boxMoved=False;
130           item=&guiApp.mainItems[SelectedItem];
131           itemtype=item->type;
132           item->pressed=btnPressed;
133           // NOTE TO MYSELF: commented, because the expression can never be true
134           /*switch( item->type )
135            {
136             case itButton:
137                  if ( ( SelectedItem > -1 ) &&
138                     ( ( ( item->message == evPlaySwitchToPause && item->message == evPauseSwitchToPlay ) ) ||
139                       ( ( item->message == evPauseSwitchToPlay && item->message == evPlaySwitchToPause ) ) ) )
140                   { item->pressed=btnDisabled; }
141                  break;
142            }*/
143           if ( itemtype == itRPotmeter )
144            {
145             prev_point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint;
146             if ( prev_point < 0.0 ) prev_point+=2*M_PI;
147             if ( prev_point <= item->arclength ) endstop=False;
148             else endstop=STOPPED_AT_0 + STOPPED_AT_100;   // block movement
149            }
150           break;
151    case wsRLMouseButton:
152           boxMoved=False;
153           if ( SelectedItem != -1 )   // NOTE TO MYSELF: only if hasButton
154            {
155             item=&guiApp.mainItems[SelectedItem];
156             item->pressed=btnReleased;
157            }
158           if ( currentselected == - 1 || SelectedItem == -1 ) { itemtype=0; break; }
159           SelectedItem=-1;
160           value=0;
161           switch( itemtype )
162            {
163             case itHPotmeter:
164                  value=100.0 * ( X - item->x ) / item->width;
165                  break;
166             case itVPotmeter:
167                  value=100.0 - 100.0 * ( Y - item->y ) / item->height;
168                  break;
169             case itRPotmeter:
170                  if ( endstop ) { itemtype=0; return; }
171                  point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint;
172                  if ( point < 0.0 ) point+=2*M_PI;
173                  value=100.0 * point / item->arclength;
174                  break;
175            }
176           uiEvent( item->message,value );
177           itemtype=0;
178           break;
179 
180    case wsRRMouseButton:
181         gtkShow( ivShowPopUpMenu, (void *) wMain );
182         break;
183 
184 /* rolled mouse ... de szar :))) */
185    case wsP5MouseButton: value=-2.5f; goto rollerhandled;
186    case wsP4MouseButton: value= 2.5f;
187 rollerhandled:
188           if (currentselected != - 1)
189            {
190             item=&guiApp.mainItems[currentselected];
191             if ( ( item->type == itHPotmeter )||( item->type == itVPotmeter )||( item->type == itRPotmeter ) )
192              {
193               item->value=constrain(item->value + value);
194               uiEvent( item->message,item->value );
195              }
196            }
197           break;
198 
199 /* moving */
200    case wsMoveMouse:
201           item=&guiApp.mainItems[SelectedItem];
202           switch ( itemtype )
203            {
204             case itPLMButton:
205                  wsWindowMove( &guiApp.mainWindow,True,RX - abs( sx ),RY - abs( sy ) );
206                  break;
207             case itPRMButton:
208                  if (guiApp.menuIsPresent) guiApp.menuWindow.MouseHandler( 0,RX,RY,0,0 );
209                  break;
210             case itRPotmeter:
211                  point=appRadian( item, X - item->x, Y - item->y ) - item->zeropoint;
212                  if ( point < 0.0 ) point+=2*M_PI;
213                  if ( item->arclength < 2 * M_PI )
214                  /* a potmeter with separated 0% and 100% positions */
215                   {
216                    value=item->value;
217                    if ( point - prev_point > M_PI )
218                    /* turned beyond the 0% position */
219                     {
220                      if ( !endstop )
221                       {
222                        endstop=STOPPED_AT_0;
223                        value=0.0f;
224                       }
225                     }
226                    else if ( prev_point - point > M_PI )
227                    /* turned back from beyond the 0% position */
228                     {
229                      if ( endstop == STOPPED_AT_0 ) endstop=False;
230                     }
231                    else if ( prev_point <= item->arclength && point > item->arclength )
232                    /* turned beyond the 100% position */
233                     {
234                      if ( !endstop )
235                       {
236                        endstop=STOPPED_AT_100;
237                        value=100.0f;
238                       }
239                     }
240                    else if ( prev_point > item->arclength && point <= item->arclength )
241                    /* turned back from beyond the 100% position */
242                     {
243                      if ( endstop == STOPPED_AT_100 ) endstop=False;
244                     }
245                   }
246                  if ( !endstop ) value=100.0 * point / item->arclength;
247                  prev_point=point;
248                  goto potihandled;
249             case itVPotmeter:
250                  value=100.0 - 100.0 * ( Y - item->y ) / item->height;
251                  goto potihandled;
252             case itHPotmeter:
253                  value=100.0 * ( X - item->x ) / item->width;
254 potihandled:
255                  item->value=constrain(value);
256                  uiEvent( item->message,item->value );
257                  break;
258            }
259           break;
260   }
261 }
262 
uiMainKey(int KeyCode,int Type,int Key)263 static void uiMainKey( int KeyCode,int Type,int Key )
264 {
265  int msg = evNone;
266 
267  if ( Type != wsKeyPressed ) return;
268 
269  if ( !Key )
270   {
271    switch ( KeyCode )
272     {
273      // NOTE TO MYSELF: This is only for the Acer AirKey V keyboard.
274    /*case wsXFMMPrev:     msg=evPrev;              break;
275      case wsXFMMStop:     msg=evStop;              break;
276      case wsXFMMPlay:     msg=evPlaySwitchToPause; break;
277      case wsXFMMNext:     msg=evNext;              break;
278      case wsXFMMVolUp:    msg=evIncVolume;         break;
279      case wsXFMMVolDown:  msg=evDecVolume;         break;
280      case wsXFMMMute:     msg=evMute;              break;*/
281     }
282   }
283   else
284    {
285     switch ( Key )
286      {
287       case wsEnter:            msg=evPlay; break;
288       case wsXF86LowerVolume:  msg=evDecVolume; break;
289       case wsXF86RaiseVolume:  msg=evIncVolume; break;
290       case wsXF86Mute:         msg=evMute; break;
291       case wsXF86Pause:
292       case wsXF86Play:         msg=evPlaySwitchToPause; break;
293       case wsXF86Stop:         msg=evStop; break;
294       case wsXF86Prev:         msg=evPrev; break;
295       case wsXF86Next:         msg=evNext; break;
296       case wsXF86Media:        msg=evLoad; break;
297       case wsEscape:
298             if ( guiInfo.VideoWindow && guiInfo.Playing && guiApp.videoWindow.isFullScreen )
299              {
300               uiEvent( evNormalSize,0 );
301               return;
302              }
303       default:          vo_x11_putkey( Key ); return;
304      }
305    }
306  if ( msg != evNone ) uiEvent( msg,0 );
307 }
308 
309 /* this will be used to handle drag & drop files */
uiMainDND(int num,char ** files)310 static void uiMainDND(int num,char** files)
311 {
312   struct stat buf;
313   int f = 0;
314 
315   char* subtitles = NULL;
316   char* file = NULL;
317   char* s;
318 
319   if (num <= 0)
320     return;
321 
322 
323   /* now fill it with new items */
324   for(f=0; f < num; f++){
325     char* str = strdup( files[f] );
326     plItem* item;
327 
328     url_unescape_string(str, files[f]);
329 
330     if(stat(str,&buf) == 0 && S_ISDIR(buf.st_mode) == 0) {
331       /* this is not a directory so try to play it */
332       mp_msg( MSGT_GPLAYER,MSGL_V,"Received D&D %s\n",str );
333 
334       /* check if it is a subtitle file */
335       {
336         char* ext = strrchr(str,'.');
337         if (ext) {
338           static char supported[] = "utf/sub/srt/smi/rt//txt/ssa/aqt/";
339           char* type;
340           int len;
341           if((len=strlen(++ext)) && (type=strstr(supported,ext)) &&\
342              (type-supported)%4 == 0 && *(type+len) == '/'){
343             /* handle subtitle file */
344             nfree(subtitles);
345             subtitles = str;
346             continue;
347           }
348         }
349       }
350 
351       /* clear playlist */
352       if (file == NULL) {
353         file = files[f];
354         listMgr(PLAYLIST_DELETE,0);
355       }
356 
357       item = calloc(1,sizeof(*item));
358 
359       s = strrchr( str,'/' );
360 
361       /* FIXME: decompose file name ? */
362       /* yes -- Pontscho */
363       if ( s ) {
364         *s=0; s++;
365         item->name = gstrdup( s );
366         item->path = gstrdup( str );
367       } else {
368         // NOTE TO MYSELF: this shouldn't happen, make sure we have a full path
369         item->name = strdup(str);
370         item->path = strdup(".");
371       }
372       listMgr(PLAYLIST_ITEM_APPEND,item);
373     } else {
374       mp_msg( MSGT_GPLAYER,MSGL_WARN,_(MSGTR_GUI_MSG_NotAFile1),str );
375     }
376     free( str );
377   }
378 
379   if (file) {
380     uiSetFile( NULL,file,STREAMTYPE_FILE );
381     if ( guiInfo.Playing == GUI_PLAY ) uiEvent( evStop,0 );
382     uiEvent( evPlay,0 );
383   }
384   if (subtitles) {
385     nfree(guiInfo.SubtitleFilename);
386     guiInfo.SubtitleFilename = subtitles;
387     mplayerLoadSubtitle(guiInfo.SubtitleFilename);
388   }
389 }
390 
uiMainInit(void)391 void uiMainInit (void)
392 {
393   mainDrawBuffer = malloc(guiApp.main.Bitmap.ImageSize);
394 
395   if (!mainDrawBuffer)
396   {
397     char msg[80] = "[main] ";
398 
399     av_strlcat(msg, _(MSGTR_GUI_MSG_MemoryErrorWindow), sizeof(msg));
400     gmp_msg(MSGT_GPLAYER, MSGL_FATAL, msg);
401     mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
402   }
403 
404   wsWindowCreate(&guiApp.mainWindow, guiApp.main.x, guiApp.main.y, guiApp.main.width, guiApp.main.height, (guiApp.mainDecoration ? wsShowFrame : 0 ) | wsMinSize | wsMaxSize | wsHideWindow, wsShowMouseCursor | wsHandleMouseButton | wsHandleMouseMove, MPlayer);
405   mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[main] mainWindow ID: 0x%x\n", (int) guiApp.mainWindow.WindowID);
406   wsWindowShape(&guiApp.mainWindow, guiApp.main.Mask.Image);
407   wsWindowIcon(wsDisplay, guiApp.mainWindow.WindowID, &guiIcon);
408   wsXDNDMakeAwareness(&guiApp.mainWindow);
409 
410   guiApp.mainWindow.DrawHandler = uiMainDraw;
411   guiApp.mainWindow.MouseHandler = uiMainMouse;
412   guiApp.mainWindow.KeyHandler = uiMainKey;
413   guiApp.mainWindow.DNDHandler = uiMainDND;
414 }
415 
uiMainDone(void)416 void uiMainDone (void)
417 {
418   nfree(mainDrawBuffer);
419   wsWindowDestroy(&guiApp.mainWindow);
420   wsEvents();
421 }
422