@@ -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
@@ -312,9 +328,9 @@ static size_t write_memory_callback(void *contents, size_t size, size_t nmemb, v
312328 return realsize ;
313329}
314330
315- static void DownloadTLESource (CURL * curl , const char * url , FILE * out )
331+ static bool DownloadTLESource (CURL * curl , const char * url , FILE * out )
316332{
317- if (!curl || !url || !out ) return ;
333+ if (!curl || !url || !out ) return false ;
318334
319335 struct MemoryStruct chunk ;
320336 chunk .memory = malloc (1 );
@@ -324,7 +340,7 @@ static void DownloadTLESource(CURL *curl, const char *url, FILE *out)
324340 curl_easy_setopt (curl , CURLOPT_WRITEDATA , (void * )& chunk );
325341 curl_easy_setopt (curl , CURLOPT_WRITEFUNCTION , write_memory_callback );
326342 curl_easy_setopt (curl , CURLOPT_FOLLOWLOCATION , 1L );
327- curl_easy_setopt (curl , CURLOPT_ACCEPT_ENCODING , "" ); // handle compression
343+ curl_easy_setopt (curl , CURLOPT_ACCEPT_ENCODING , "" ); /* handle compression */
328344 curl_easy_setopt (curl , CURLOPT_USERAGENT , "Mozilla 5.0 (compatible; TLEscope/3.X; +https://github.com/aweeri/TLEscope)" ); //TODO: add version whenever aval internally
329345
330346#if defined(_WIN32 ) || defined(_WIN64 )
@@ -335,7 +351,8 @@ static void DownloadTLESource(CURL *curl, const char *url, FILE *out)
335351 long http_code = 0 ;
336352 curl_easy_getinfo (curl , CURLINFO_RESPONSE_CODE , & http_code );
337353
338- if (res == CURLE_OK && http_code == 200 )
354+ bool ok = (res == CURLE_OK && http_code == 200 );
355+ if (ok )
339356 {
340357 fwrite (chunk .memory , 1 , chunk .size , out );
341358 fprintf (out , "\r\n" );
@@ -346,6 +363,7 @@ static void DownloadTLESource(CURL *curl, const char *url, FILE *out)
346363 }
347364
348365 free (chunk .memory );
366+ return ok ;
349367}
350368
351369static void ReloadTLEsLocally (UIContext * ctx , AppConfig * cfg )
@@ -366,45 +384,89 @@ static void ReloadTLEsLocally(UIContext *ctx, AppConfig *cfg)
366384 LoadSatSelection ();
367385}
368386
369- static void PullTLEData (UIContext * ctx , AppConfig * cfg )
387+ /* background thread: downloads all selected TLE sources to data.tle */
388+ static void * PullTLEThread (void * arg )
370389{
390+ (void )arg ;
391+ AppConfig * cfg = pull_cfg ;
392+
371393 FILE * out = fopen ("data.tle" , "wb" );
372- if (out )
394+ if (!out )
395+ {
396+ pull_state = PULL_ERROR ;
397+ return NULL ;
398+ }
399+
400+ unsigned int mask = 0 , ret_mask = 0 , cust_mask = 0 ;
401+ for (int i = 0 ; i < 25 ; i ++ )
402+ if (celestrak_selected [i ]) mask |= (1 << i );
403+ for (int i = 0 ; i < NUM_RETLECTOR_SOURCES ; i ++ )
404+ if (retlector_selected [i ]) ret_mask |= (1 << i );
405+ for (int i = 0 ; i < cfg -> custom_tle_source_count ; i ++ )
406+ if (cfg -> custom_tle_sources [i ].selected ) cust_mask |= (1 << i );
407+
408+ fprintf (out , "# EPOCH:%ld MASK:%u CUST_MASK:%u RET_MASK:%u\r\n" , (long )time (NULL ), mask , cust_mask , ret_mask );
409+
410+ int ok_count = 0 , fail_count = 0 ;
411+ CURL * curl = curl_easy_init ();
412+ if (curl )
373413 {
374- unsigned int mask = 0 , ret_mask = 0 , cust_mask = 0 ;
375- for (int i = 0 ; i < 25 ; i ++ )
376- if (celestrak_selected [i ]) mask |= (1 << i );
377414 for (int i = 0 ; i < NUM_RETLECTOR_SOURCES ; i ++ )
378- if (retlector_selected [i ]) ret_mask |= (1 << i );
415+ if (ret_mask & (1 << i ))
416+ { if (DownloadTLESource (curl , RETLECTOR_SOURCES [i ].url , out )) ok_count ++ ; else fail_count ++ ; }
417+
418+ for (int i = 0 ; i < 25 ; i ++ )
419+ if (mask & (1 << i ))
420+ { if (DownloadTLESource (curl , SOURCES [i ].url , out )) ok_count ++ ; else fail_count ++ ; }
421+
379422 for (int i = 0 ; i < cfg -> custom_tle_source_count ; i ++ )
380- if (cfg -> custom_tle_sources [i ].selected ) cust_mask |= (1 << i );
423+ if (cust_mask & (1 << i ))
424+ { if (DownloadTLESource (curl , cfg -> custom_tle_sources [i ].url , out )) ok_count ++ ; else fail_count ++ ; }
381425
382- fprintf (out , "# EPOCH:%ld MASK:%u CUST_MASK:%u RET_MASK:%u\r\n" , (long )time (NULL ), mask , cust_mask , ret_mask );
426+ curl_easy_cleanup (curl );
427+ }
428+ else
429+ {
430+ printf ("Failed to initialize libcurl.\n" );
431+ }
383432
384- CURL * curl = curl_easy_init ();
385- if (curl )
386- {
387- for (int i = 0 ; i < NUM_RETLECTOR_SOURCES ; i ++ )
388- if (retlector_selected [i ])
389- DownloadTLESource (curl , RETLECTOR_SOURCES [i ].url , out );
433+ fclose (out );
390434
391- for (int i = 0 ; i < 25 ; i ++ )
392- if (celestrak_selected [i ])
393- DownloadTLESource (curl , SOURCES [i ].url , out );
435+ pull_partial = (ok_count > 0 && fail_count > 0 );
436+ __sync_synchronize (); /* ensure pull_partial is visible before pull_state on ARM */
437+ if (fail_count > 0 && ok_count == 0 ) pull_state = PULL_ERROR ;
438+ else pull_state = PULL_DONE ;
439+ return NULL ;
440+ }
394441
395- for ( int i = 0 ; i < cfg -> custom_tle_source_count ; i ++ )
396- if ( cfg -> custom_tle_sources [ i ]. selected )
397- DownloadTLESource ( curl , cfg -> custom_tle_sources [ i ]. url , out );
442+ #if defined( _WIN32 ) || defined( _WIN64 )
443+ static void PullTLEThreadWin ( void * arg ) { PullTLEThread ( arg ); }
444+ #endif
398445
399- curl_easy_cleanup (curl );
400- }
401- else
402- {
403- printf ("Failed to initialize libcurl.\n" );
404- }
446+ /* called from main thread to kick off async pull */
447+ static void PullTLEData (AppConfig * cfg )
448+ {
449+ if (pull_state == PULL_BUSY ) return ;
450+ pull_state = PULL_BUSY ;
451+ pull_partial = false;
452+ pull_cfg = cfg ;
405453
406- fclose (out );
454+ #if defined(_WIN32 ) || defined(_WIN64 )
455+ uintptr_t h = _beginthread (PullTLEThreadWin , 0 , NULL );
456+ if (h == (uintptr_t )-1L ) { pull_state = PULL_ERROR ; return ; }
457+ pull_thread = (HANDLE )h ;
458+ #else
459+ if (pthread_create (& pull_thread , NULL , PullTLEThread , NULL ) != 0 )
460+ { pull_state = PULL_ERROR ; return ; }
461+ pthread_detach (pull_thread );
462+ #endif
463+ }
407464
465+ /* called each frame from DrawGUI to finish reload on the main thread */
466+ static void FinishPullIfDone (UIContext * ctx , AppConfig * cfg )
467+ {
468+ if (pull_state == PULL_DONE )
469+ {
408470 if (ctx )
409471 {
410472 * ctx -> selected_sat = NULL ;
@@ -425,6 +487,7 @@ static void PullTLEData(UIContext *ctx, AppConfig *cfg)
425487 }
426488 LoadSatSelection ();
427489 data_tle_epoch = time (NULL );
490+ pull_state = PULL_IDLE ;
428491 }
429492}
430493
@@ -969,6 +1032,8 @@ static bool DrawMaterialWindow(Rectangle bounds, const char *title, AppConfig *c
9691032/* main ui rendering loop */
9701033void DrawGUI (UIContext * ctx , AppConfig * cfg , Font customFont )
9711034{
1035+ FinishPullIfDone (ctx , cfg );
1036+
9721037 * ctx -> show_scope = show_scope_dialog ;
9731038 * ctx -> scope_az = scope_az ;
9741039 * ctx -> scope_el = scope_el ;
@@ -1600,9 +1665,17 @@ void DrawGUI(UIContext *ctx, AppConfig *cfg, Font customFont)
16001665 }
16011666 DrawUIText (customFont , age_str , tm_x + 10 * cfg -> ui_scale , tm_y + 35 * cfg -> ui_scale , 16 * cfg -> ui_scale , cfg -> text_main );
16021667
1603- 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" ))
16041668 {
1605- PullTLEData (ctx , cfg );
1669+ const char * btn_label = "Apply" ;
1670+ if (pull_state == PULL_BUSY ) btn_label = "Pulling.." ;
1671+ else if (pull_state == PULL_ERROR ) btn_label = "Error" ;
1672+ else if (pull_partial ) btn_label = "Partial" ;
1673+ if (pull_state == PULL_BUSY ) GuiDisable ();
1674+ 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 ))
1675+ {
1676+ PullTLEData (cfg );
1677+ }
1678+ if (pull_state == PULL_BUSY ) GuiEnable ();
16061679 }
16071680
16081681 float total_height = 28 * cfg -> ui_scale + (retlector_expanded ? NUM_RETLECTOR_SOURCES * 25 * cfg -> ui_scale : 0 );
@@ -3213,10 +3286,13 @@ case WND_SCOPE:
32133286 float spacing = 15 * cfg -> ui_scale ;
32143287 float startX = tleWarnWindow .x + (tleWarnWindow .width - (3 * btnWidth + 2 * spacing )) / 2.0f ;
32153288
3216- if (GuiButton ((Rectangle ){startX , tleWarnWindow .y + 105 * cfg -> ui_scale , btnWidth , 35 * cfg -> ui_scale }, "#112# Update All" )) {
3217- PullTLEData (ctx , cfg );
3289+ if (pull_state == PULL_BUSY ) GuiDisable ();
3290+ if (GuiButton ((Rectangle ){startX , tleWarnWindow .y + 105 * cfg -> ui_scale , btnWidth , 35 * cfg -> ui_scale },
3291+ pull_state == PULL_BUSY ? "Pulling.." : "#112# Update All" )) {
3292+ PullTLEData (cfg );
32183293 show_tle_warning = false;
32193294 }
3295+ if (pull_state == PULL_BUSY ) GuiEnable ();
32203296 if (GuiButton ((Rectangle ){startX + btnWidth + spacing , tleWarnWindow .y + 105 * cfg -> ui_scale , btnWidth , 35 * cfg -> ui_scale }, "#1# Manage" )) {
32213297 show_tle_warning = false;
32223298 if (!show_tle_mgr_dialog ) {
0 commit comments