@@ -23,6 +23,12 @@ typedef struct tagMSG *LPMSG;
2323
2424//TODO: explain better what in gods name happened above
2525
26+ #if defined(_WIN32 ) || defined(_WIN64 )
27+ #include <process.h>
28+ #else
29+ #include <pthread.h>
30+ #endif
31+
2632#define RAYGUI_IMPLEMENTATION
2733#include "../lib/raygui.h"
2834
@@ -156,6 +162,16 @@ static bool manual_expanded = true;
156162static bool celestrak_selected [25 ] = {false};
157163static long data_tle_epoch = -1 ;
158164
165+ enum { PULL_IDLE = 0 , PULL_BUSY , PULL_DONE , PULL_ERROR };
166+ static volatile int pull_state = PULL_IDLE ;
167+ static volatile bool pull_partial = false;
168+ static AppConfig * pull_cfg = NULL ;
169+ #if defined(_WIN32 ) || defined(_WIN64 )
170+ static HANDLE pull_thread = NULL ;
171+ #else
172+ static pthread_t pull_thread ;
173+ #endif
174+
159175static char new_tle_buf [512 ] = "" ;
160176static bool edit_new_tle = false;
161177
@@ -311,9 +327,9 @@ static size_t write_memory_callback(void *contents, size_t size, size_t nmemb, v
311327 return realsize ;
312328}
313329
314- static void DownloadTLESource (CURL * curl , const char * url , FILE * out )
330+ static bool DownloadTLESource (CURL * curl , const char * url , FILE * out )
315331{
316- if (!curl || !url || !out ) return ;
332+ if (!curl || !url || !out ) return false ;
317333
318334 struct MemoryStruct chunk ;
319335 chunk .memory = malloc (1 );
@@ -323,7 +339,7 @@ static void DownloadTLESource(CURL *curl, const char *url, FILE *out)
323339 curl_easy_setopt (curl , CURLOPT_WRITEDATA , (void * )& chunk );
324340 curl_easy_setopt (curl , CURLOPT_WRITEFUNCTION , write_memory_callback );
325341 curl_easy_setopt (curl , CURLOPT_FOLLOWLOCATION , 1L );
326- curl_easy_setopt (curl , CURLOPT_ACCEPT_ENCODING , "" ); // handle compression
342+ curl_easy_setopt (curl , CURLOPT_ACCEPT_ENCODING , "" ); /* handle compression */
327343 curl_easy_setopt (curl , CURLOPT_USERAGENT , "Mozilla 5.0 (compatible; TLEscope/3.X; +https://github.com/aweeri/TLEscope)" ); //TODO: add version whenever aval internally
328344
329345#if defined(_WIN32 ) || defined(_WIN64 )
@@ -334,7 +350,8 @@ static void DownloadTLESource(CURL *curl, const char *url, FILE *out)
334350 long http_code = 0 ;
335351 curl_easy_getinfo (curl , CURLINFO_RESPONSE_CODE , & http_code );
336352
337- if (res == CURLE_OK && http_code == 200 )
353+ bool ok = (res == CURLE_OK && http_code == 200 );
354+ if (ok )
338355 {
339356 fwrite (chunk .memory , 1 , chunk .size , out );
340357 fprintf (out , "\r\n" );
@@ -345,6 +362,7 @@ static void DownloadTLESource(CURL *curl, const char *url, FILE *out)
345362 }
346363
347364 free (chunk .memory );
365+ return ok ;
348366}
349367
350368static void ReloadTLEsLocally (UIContext * ctx , AppConfig * cfg )
@@ -365,45 +383,88 @@ static void ReloadTLEsLocally(UIContext *ctx, AppConfig *cfg)
365383 LoadSatSelection ();
366384}
367385
368- static void PullTLEData (UIContext * ctx , AppConfig * cfg )
386+ /* background thread: downloads all selected TLE sources to data.tle */
387+ static void * PullTLEThread (void * arg )
369388{
389+ (void )arg ;
390+ AppConfig * cfg = pull_cfg ;
391+
370392 FILE * out = fopen ("data.tle" , "wb" );
371- if (out )
393+ if (!out )
394+ {
395+ pull_state = PULL_ERROR ;
396+ return NULL ;
397+ }
398+
399+ unsigned int mask = 0 , ret_mask = 0 , cust_mask = 0 ;
400+ for (int i = 0 ; i < 25 ; i ++ )
401+ if (celestrak_selected [i ]) mask |= (1 << i );
402+ for (int i = 0 ; i < NUM_RETLECTOR_SOURCES ; i ++ )
403+ if (retlector_selected [i ]) ret_mask |= (1 << i );
404+ for (int i = 0 ; i < cfg -> custom_tle_source_count ; i ++ )
405+ if (cfg -> custom_tle_sources [i ].selected ) cust_mask |= (1 << i );
406+
407+ fprintf (out , "# EPOCH:%ld MASK:%u CUST_MASK:%u RET_MASK:%u\r\n" , (long )time (NULL ), mask , cust_mask , ret_mask );
408+
409+ int ok_count = 0 , fail_count = 0 ;
410+ CURL * curl = curl_easy_init ();
411+ if (curl )
372412 {
373- unsigned int mask = 0 , ret_mask = 0 , cust_mask = 0 ;
374- for (int i = 0 ; i < 25 ; i ++ )
375- if (celestrak_selected [i ]) mask |= (1 << i );
376413 for (int i = 0 ; i < NUM_RETLECTOR_SOURCES ; i ++ )
377- if (retlector_selected [i ]) ret_mask |= (1 << i );
414+ if (ret_mask & (1 << i ))
415+ { if (DownloadTLESource (curl , RETLECTOR_SOURCES [i ].url , out )) ok_count ++ ; else fail_count ++ ; }
416+
417+ for (int i = 0 ; i < 25 ; i ++ )
418+ if (mask & (1 << i ))
419+ { if (DownloadTLESource (curl , SOURCES [i ].url , out )) ok_count ++ ; else fail_count ++ ; }
420+
378421 for (int i = 0 ; i < cfg -> custom_tle_source_count ; i ++ )
379- if (cfg -> custom_tle_sources [i ].selected ) cust_mask |= (1 << i );
422+ if (cust_mask & (1 << i ))
423+ { if (DownloadTLESource (curl , cfg -> custom_tle_sources [i ].url , out )) ok_count ++ ; else fail_count ++ ; }
380424
381- fprintf (out , "# EPOCH:%ld MASK:%u CUST_MASK:%u RET_MASK:%u\r\n" , (long )time (NULL ), mask , cust_mask , ret_mask );
425+ curl_easy_cleanup (curl );
426+ }
427+ else
428+ {
429+ printf ("Failed to initialize libcurl.\n" );
430+ }
382431
383- CURL * curl = curl_easy_init ();
384- if (curl )
385- {
386- for (int i = 0 ; i < NUM_RETLECTOR_SOURCES ; i ++ )
387- if (retlector_selected [i ])
388- DownloadTLESource (curl , RETLECTOR_SOURCES [i ].url , out );
432+ fclose (out );
389433
390- for (int i = 0 ; i < 25 ; i ++ )
391- if (celestrak_selected [i ])
392- DownloadTLESource (curl , SOURCES [i ].url , out );
434+ pull_partial = (ok_count > 0 && fail_count > 0 );
435+ __sync_synchronize (); /* ensure pull_partial is visible before pull_state on ARM */
436+ pull_state = (ok_count > 0 ) ? PULL_DONE : PULL_ERROR ;
437+ return NULL ;
438+ }
393439
394- for ( int i = 0 ; i < cfg -> custom_tle_source_count ; i ++ )
395- if ( cfg -> custom_tle_sources [ i ]. selected )
396- DownloadTLESource ( curl , cfg -> custom_tle_sources [ i ]. url , out );
440+ #if defined( _WIN32 ) || defined( _WIN64 )
441+ static void PullTLEThreadWin ( void * arg ) { PullTLEThread ( arg ); }
442+ #endif
397443
398- curl_easy_cleanup (curl );
399- }
400- else
401- {
402- printf ("Failed to initialize libcurl.\n" );
403- }
444+ /* called from main thread to kick off async pull */
445+ static void PullTLEData (AppConfig * cfg )
446+ {
447+ if (pull_state == PULL_BUSY ) return ;
448+ pull_state = PULL_BUSY ;
449+ pull_partial = false;
450+ pull_cfg = cfg ;
404451
405- fclose (out );
452+ #if defined(_WIN32 ) || defined(_WIN64 )
453+ uintptr_t h = _beginthread (PullTLEThreadWin , 0 , NULL );
454+ if (h == (uintptr_t )-1L ) { pull_state = PULL_ERROR ; return ; }
455+ pull_thread = (HANDLE )h ;
456+ #else
457+ if (pthread_create (& pull_thread , NULL , PullTLEThread , NULL ) != 0 )
458+ { pull_state = PULL_ERROR ; return ; }
459+ pthread_detach (pull_thread );
460+ #endif
461+ }
406462
463+ /* called each frame from DrawGUI to finish reload on the main thread */
464+ static void FinishPullIfDone (UIContext * ctx , AppConfig * cfg )
465+ {
466+ if (pull_state == PULL_DONE )
467+ {
407468 if (ctx )
408469 {
409470 * ctx -> selected_sat = NULL ;
@@ -424,6 +485,7 @@ static void PullTLEData(UIContext *ctx, AppConfig *cfg)
424485 }
425486 LoadSatSelection ();
426487 data_tle_epoch = time (NULL );
488+ pull_state = PULL_IDLE ;
427489 }
428490}
429491
@@ -968,6 +1030,8 @@ static bool DrawMaterialWindow(Rectangle bounds, const char *title, AppConfig *c
9681030/* main ui rendering loop */
9691031void DrawGUI (UIContext * ctx , AppConfig * cfg , Font customFont )
9701032{
1033+ FinishPullIfDone (ctx , cfg );
1034+
9711035 * ctx -> show_scope = show_scope_dialog ;
9721036 * ctx -> scope_az = scope_az ;
9731037 * ctx -> scope_el = scope_el ;
@@ -1599,9 +1663,17 @@ void DrawGUI(UIContext *ctx, AppConfig *cfg, Font customFont)
15991663 }
16001664 DrawUIText (customFont , age_str , tm_x + 10 * cfg -> ui_scale , tm_y + 35 * cfg -> ui_scale , 16 * cfg -> ui_scale , cfg -> text_main );
16011665
1602- if (GuiButton ((Rectangle ){tm_x + tmMgrWindow .width - 110 * cfg -> ui_scale , tm_y + 30 * cfg -> ui_scale , 100 * cfg -> ui_scale , 26 * cfg -> ui_scale }, "Apply" ))
16031666 {
1604- PullTLEData (ctx , cfg );
1667+ const char * btn_label = "Apply" ;
1668+ if (pull_state == PULL_BUSY ) btn_label = "Pulling.." ;
1669+ else if (pull_state == PULL_ERROR ) btn_label = "Error" ;
1670+ else if (pull_partial ) btn_label = "Partial" ;
1671+ if (pull_state == PULL_BUSY ) GuiDisable ();
1672+ if (GuiButton ((Rectangle ){tm_x + tmMgrWindow .width - 110 * cfg -> ui_scale , tm_y + 30 * cfg -> ui_scale , 100 * cfg -> ui_scale , 26 * cfg -> ui_scale }, btn_label ))
1673+ {
1674+ PullTLEData (cfg );
1675+ }
1676+ if (pull_state == PULL_BUSY ) GuiEnable ();
16051677 }
16061678
16071679 float total_height = 28 * cfg -> ui_scale + (retlector_expanded ? NUM_RETLECTOR_SOURCES * 25 * cfg -> ui_scale : 0 );
@@ -3205,10 +3277,13 @@ case WND_SCOPE:
32053277 float spacing = 15 * cfg -> ui_scale ;
32063278 float startX = tleWarnWindow .x + (tleWarnWindow .width - (3 * btnWidth + 2 * spacing )) / 2.0f ;
32073279
3208- if (GuiButton ((Rectangle ){startX , tleWarnWindow .y + 105 * cfg -> ui_scale , btnWidth , 35 * cfg -> ui_scale }, "#112# Update All" )) {
3209- PullTLEData (ctx , cfg );
3280+ if (pull_state == PULL_BUSY ) GuiDisable ();
3281+ if (GuiButton ((Rectangle ){startX , tleWarnWindow .y + 105 * cfg -> ui_scale , btnWidth , 35 * cfg -> ui_scale },
3282+ pull_state == PULL_BUSY ? "Pulling.." : "#112# Update All" )) {
3283+ PullTLEData (cfg );
32103284 show_tle_warning = false;
32113285 }
3286+ if (pull_state == PULL_BUSY ) GuiEnable ();
32123287 if (GuiButton ((Rectangle ){startX + btnWidth + spacing , tleWarnWindow .y + 105 * cfg -> ui_scale , btnWidth , 35 * cfg -> ui_scale }, "#1# Manage" )) {
32133288 show_tle_warning = false;
32143289 if (!show_tle_mgr_dialog ) {
0 commit comments