OSDN Git Service

Support group affiliation with component package granularity.
[mingw/mingw-get.git] / src / pkgnget.cpp
1 /*
2  * pkgnget.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2012, 2013, MinGW.org Project
8  *
9  *
10  * Implementation of the network download agent interface, through
11  * which the mingw-get GUI requests the services of the mingw-get DLL
12  * to get (download) package archives from internet repositories.
13  *
14  *
15  * This is free software.  Permission is granted to copy, modify and
16  * redistribute this software, under the provisions of the GNU General
17  * Public License, Version 3, (or, at your option, any later version),
18  * as published by the Free Software Foundation; see the file COPYING
19  * for licensing details.
20  *
21  * Note, in particular, that this software is provided "as is", in the
22  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
25  * MinGW Project, accept liability for any damages, however caused,
26  * arising from the use of this software.
27  *
28  */
29 #include "guimain.h"
30 #include "pkgbase.h"
31 #include "pkginet.h"
32
33 #include <process.h>
34
35 class pkgDownloadMeterGUI: public pkgDownloadMeter
36 {
37   /* A locally defined class, through which the download agent
38    * may transmit progress monitoring data to the designated GUI
39    * download monitoring dialogue box.
40    */
41   public:
42     pkgDownloadMeterGUI( HWND );
43
44     virtual void ResetGUI( const char *, unsigned long );
45     virtual int Update( unsigned long );
46
47     inline ~pkgDownloadMeterGUI();
48
49   private:
50     static int spin_index, spin_active;
51     HWND file_name, file_size, copy_size, copy_frac, progress_bar;
52     virtual void SpinWaitAction( int, const char * );
53     static const char *host; static HWND status_hook;
54     static void SpinWait( void * );
55 };
56
57 /* pkgDownloadMeterGUI needs a prototype for the dmh_setpty() function;
58  * this isn't declared in any DMH specific header, since it requires a
59  * declaration of HWND, but DMH prefers to avoid namespace pollution by
60  * such windows specific type definitions, so declare it here.
61  */
62 EXTERN_C void dmh_setpty( HWND );
63
64 /* Constructor...
65  */
66 pkgDownloadMeterGUI::pkgDownloadMeterGUI( HWND dialogue )
67 {
68   /* Establish the global reference, through which the download
69    * agent will obtain access to the GUI dialogue...
70    */
71   primary = this;
72
73   /* ...and store references to its data controls.
74    */
75   file_name = GetDlgItem( dialogue, IDD_PROGRESS_MSG );
76   file_size = GetDlgItem( dialogue, IDD_PROGRESS_MAX );
77   copy_size = GetDlgItem( dialogue, IDD_PROGRESS_VAL );
78   copy_frac = GetDlgItem( dialogue, IDD_PROGRESS_PCT );
79   progress_bar = GetDlgItem( dialogue, IDD_PROGRESS_BAR );
80
81   /* Attach a pseudo-terminal emulating window, for capture
82    * of download specific diagnostic messages.
83    */
84   dmh_setpty( GetDlgItem( dialogue, IDD_DMH_CONSOLE ) );
85 }
86
87 /* Initialise static variables, used to maintain status for
88  * the SpinWait() method.
89  */
90 int pkgDownloadMeterGUI::spin_index = 0;
91 int pkgDownloadMeterGUI::spin_active = 0;
92
93 /* Likewise, its static reference pointers.
94  */
95 const char *pkgDownloadMeterGUI::host = NULL;
96 HWND pkgDownloadMeterGUI::status_hook = NULL;
97
98 void pkgDownloadMeterGUI::SpinWait( void * )
99 {
100   /* Static method; this provides the thread procedure,
101    * through which the spin wait status notification is
102    * written to the dialogue box.
103    */
104   static const char *marker = "|/-\\";
105   static const char *msg = "Connecting to %s ... %c";
106
107   /* Provide a local buffer, in which the spin wait status
108    * notification message may be formatted.
109    */
110   char status_text[1 + snprintf( NULL, 0, msg, host, marker[ spin_index ])];
111
112   /* Prepare and display the notification message...
113    */
114   spin_active = 1; spin_index = 0;
115   do { sprintf( status_text, msg, host, marker[ spin_index ] );
116        SendMessage( status_hook, WM_SETTEXT, 0, (LPARAM)(status_text) );
117        /*
118         * ...then refresh it at one second intervals, using
119         * a different marker character for each cycle...
120         */
121        Sleep( 1000 ); spin_index = (1 + spin_index) % 4;
122        /*
123         * ...until requested to terminate the thread.
124         */
125      } while( spin_active == 1 );
126
127   /* When done, release the buffer used to pass the download
128    * host domain identification, and mark the state as idle.
129    */
130   free( (void *)(host) ); host = NULL;
131   spin_active = 0;
132 }
133
134 void pkgDownloadMeterGUI::SpinWaitAction( int run, const char *uri )
135 {
136   /* Dispatcher method, used to start and stop the preceding
137    * thread procedure.
138    */
139   if( run == 0 )
140   {
141     /* This is a "stop" request; provided there is a spin-wait
142      * thread active, we signal it to stop, then wait for it to
143      * acknowledge that it has done so.
144      */
145     if( spin_active == 1 )
146       spin_active = 2;
147     while( spin_active == 2 )
148       Sleep( 100 );
149   }
150
151   else if( spin_active == 0 )
152   {
153     /* This is a "start" request; before we dispatch it, we
154      * must identify the domain name for the download host, so
155      * that it may be displayed in the notification message.
156      */
157     for( int i = 5; i > 3; i-- )
158       /*
159        * First, we identify any prefixed protocol designator...
160        */
161       if( strncasecmp( "https", uri, i ) == 0 ) { uri += i; i = 1; }
162     if( *uri == ':' ) while( *++uri == '/' )
163       /*
164        * ...removing it, and all following field delimiter
165        * characters, from the start of the URI string...
166        */
167       ;
168     /* Then, we allocate a local buffer, of sufficient size to
169      * accommodate the remainder of the URI...
170      */
171     char buf[1 + strlen( uri )]; char *p = buf;
172
173     /* ...into which we copy just the domain name fragment...
174      */
175     while( *uri && (*uri != '/') ) *p++ = *uri++;
176
177     /* ...before terminating it, and copying to heap memory.
178      */
179     *p = '\0'; host = strdup( buf );
180
181     /* Finally, we assign the file name to be displayed, when the
182      * connection is complete, and invoke the spin wait.
183      */
184     status_hook = file_name; _beginthread( SpinWait, 0, NULL );
185   }
186 }
187
188 void pkgDownloadMeterGUI::ResetGUI( const char *filename, unsigned long size )
189 {
190   /* Method to reset the displayed content of the dialogue box to
191    * an appropriate initial state, in preparation for monitoring the
192    * download of a new archive file; the name of this file, and its
193    * anticipated size are preset, as specified by the arguments.
194    */
195   char buf[12]; SizeFormat( buf, 0 ); spin_index = 0;
196   SendMessage( file_name, WM_SETTEXT, 0, (LPARAM)(filename) );
197   SendMessage( copy_size, WM_SETTEXT, 0, (LPARAM)(buf) );
198   SizeFormat( buf, content_length = size );
199   SendMessage( file_size, WM_SETTEXT, 0, (LPARAM)(buf) );
200   SendMessage( copy_frac, WM_SETTEXT, 0, (LPARAM)("0 %") );
201   SendMessage( progress_bar, PBM_SETRANGE, 0, MAKELPARAM( 0, 100 ) );
202   SendMessage( progress_bar, PBM_SETPOS, 0, 0 );
203 }
204
205 int pkgDownloadMeterGUI::Update( unsigned long count )
206 {
207   /* Method to update the download progress report; the download
208    * agent invokes this after each block of archive data has been
209    * received from the repository host, so that this method may
210    * refresh the progress counters in the dialogue box.
211    */
212   char buf[12];
213
214   /* First, we update the display of the actual byte count...
215    */
216   SizeFormat( buf, count );
217   SendMessage( copy_size, WM_SETTEXT, 0, (LPARAM)(buf) );
218
219   /* ...then we convert that byte count to a percentage of
220    * the anticipated file size, using the result to update
221    * the percentage completed, and the progress bar.
222    */
223   count = (count * 100) / content_length;
224   if( snprintf( buf, sizeof( buf ), "%d %%", count ) >= sizeof( buf ) )
225     strcpy( buf, "*** %" );
226   SendMessage( copy_frac, WM_SETTEXT, 0, (LPARAM)(buf) );
227   SendMessage( progress_bar, PBM_SETPOS, count, 0 );
228 }
229
230 /* Destructor...
231  */
232 inline pkgDownloadMeterGUI::~pkgDownloadMeterGUI()
233 {
234   /* This must close the pseudo-terminal attachment, if any, which
235    * was established for use by the diagnostic message handler...
236    */
237   dmh_setpty( NULL );
238   
239   /* ...and reset the global reference pointer, so the download
240    * agent will not attempt to access a dialogue box which has been
241    * closed by the GUI application.
242    */
243   primary = NULL;
244 }
245
246 inline void pkgActionItem::DownloadArchiveFiles( void )
247 {
248   /* Helper method, invoked by the GUI application, to initiate
249    * an in-order traversal of the schedule of actions...
250    */
251   pkgActionItem *current;
252   if( (current = this) != NULL )
253   {
254     /* ...ensuring that the traversal commences at the first of
255      * the scheduled actions...
256      */
257     while( current->prev != NULL ) current = current->prev;
258
259     /* ...to download all requisite archive files.
260      */
261     DownloadArchiveFiles( current );
262   }
263 }
264
265 #if IMPLEMENTATION_LEVEL == PACKAGE_BASE_COMPONENT
266
267 inline void AppWindowMaker::DownloadArchiveFiles( void )
268 {
269   /* Helper method to redirect a request to initiate package
270    * download, from the controlling application window object
271    * to its associated schedule of actions object.
272    */
273   pkgData->Schedule()->DownloadArchiveFiles();
274 }
275
276 EXTERN_C void pkgInvokeDownload( void *window )
277 {
278   /* Worker thread procedure, invoked by the OnCommand() method
279    * of the application window object, when initiating the archive
280    * download process via the appropriate monitoring dialogue.
281    */
282   pkgDownloadMeterGUI metered( (HWND)(window) );
283   GetAppWindow( GetParent( (HWND)(window) ))->DownloadArchiveFiles();
284   SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
285 }
286
287 #endif
288
289 /* $RCSfile$: end of file */