1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 /* <DESC>
25 * A multi threaded application that uses a progress bar to show
26 * status. It uses Gtk+ to make a smooth pulse.
27 * </DESC>
28 */
29 /*
30 * Written by Jud Bishop after studying the other examples provided with
31 * libcurl.
32 *
33 * To compile (on a single line):
34 * gcc -ggdb `pkg-config --cflags --libs gtk+-2.0` -lcurl -lssl -lcrypto
35 * -lgthread-2.0 -dl smooth-gtk-thread.c -o smooth-gtk-thread
36 */
37
38 #include <stdio.h>
39 #include <gtk/gtk.h>
40 #include <glib.h>
41 #include <unistd.h>
42 #include <pthread.h>
43
44 #include <curl/curl.h>
45
46 #define NUMT 4
47
48 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
49 int j = 0;
50 gint num_urls = 9; /* Just make sure this is less than urls[]*/
51 const char * const urls[]= {
52 "90022",
53 "90023",
54 "90024",
55 "90025",
56 "90026",
57 "90027",
58 "90028",
59 "90029",
60 "90030"
61 };
62
write_file(void * ptr,size_t size,size_t nmemb,FILE * stream)63 size_t write_file(void *ptr, size_t size, size_t nmemb, FILE *stream)
64 {
65 return fwrite(ptr, size, nmemb, stream);
66 }
67
run_one(gchar * http,int j)68 static void run_one(gchar *http, int j)
69 {
70 FILE *outfile = fopen(urls[j], "wb");
71 CURL *curl;
72
73 curl = curl_easy_init();
74 if(curl) {
75 printf("j = %d\n", j);
76
77 /* Set the URL and transfer type */
78 curl_easy_setopt(curl, CURLOPT_URL, http);
79
80 /* Write to the file */
81 curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
82 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file);
83 curl_easy_perform(curl);
84
85 fclose(outfile);
86 curl_easy_cleanup(curl);
87 }
88 }
89
pull_one_url(void * NaN)90 void *pull_one_url(void *NaN)
91 {
92 /* protect the reading and increasing of 'j' with a mutex */
93 pthread_mutex_lock(&lock);
94 while(j < num_urls) {
95 int i = j;
96 j++;
97 pthread_mutex_unlock(&lock);
98 http = g_strdup_printf("https://example.com/%s", urls[i]);
99 if(http) {
100 run_one(http, i);
101 g_free(http);
102 }
103 pthread_mutex_lock(&lock);
104 }
105 pthread_mutex_unlock(&lock);
106 return NULL;
107 }
108
109
pulse_bar(gpointer data)110 gboolean pulse_bar(gpointer data)
111 {
112 gdk_threads_enter();
113 gtk_progress_bar_pulse(GTK_PROGRESS_BAR (data));
114 gdk_threads_leave();
115
116 /* Return true so the function is called again; returning false removes this
117 * timeout function.
118 */
119 return TRUE;
120 }
121
create_thread(void * progress_bar)122 void *create_thread(void *progress_bar)
123 {
124 pthread_t tid[NUMT];
125 int i;
126
127 /* Make sure I do not create more threads than urls. */
128 for(i = 0; i < NUMT && i < num_urls ; i++) {
129 int error = pthread_create(&tid[i],
130 NULL, /* default attributes please */
131 pull_one_url,
132 NULL);
133 if(0 != error)
134 fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error);
135 else
136 fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]);
137 }
138
139 /* Wait for all threads to terminate. */
140 for(i = 0; i < NUMT && i < num_urls; i++) {
141 pthread_join(tid[i], NULL);
142 fprintf(stderr, "Thread %d terminated\n", i);
143 }
144
145 /* This stops the pulsing if you have it turned on in the progress bar
146 section */
147 g_source_remove(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(progress_bar),
148 "pulse_id")));
149
150 /* This destroys the progress bar */
151 gtk_widget_destroy(progress_bar);
152
153 /* [Un]Comment this out to kill the program rather than pushing close. */
154 /* gtk_main_quit(); */
155
156
157 return NULL;
158
159 }
160
cb_delete(GtkWidget * window,gpointer data)161 static gboolean cb_delete(GtkWidget *window, gpointer data)
162 {
163 gtk_main_quit();
164 return FALSE;
165 }
166
main(int argc,char ** argv)167 int main(int argc, char **argv)
168 {
169 GtkWidget *top_window, *outside_frame, *inside_frame, *progress_bar;
170
171 /* Must initialize libcurl before any threads are started */
172 curl_global_init(CURL_GLOBAL_ALL);
173
174 /* Init thread */
175 g_thread_init(NULL);
176 gdk_threads_init();
177 gdk_threads_enter();
178
179 gtk_init(&argc, &argv);
180
181 /* Base window */
182 top_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
183
184 /* Frame */
185 outside_frame = gtk_frame_new(NULL);
186 gtk_frame_set_shadow_type(GTK_FRAME(outside_frame), GTK_SHADOW_OUT);
187 gtk_container_add(GTK_CONTAINER(top_window), outside_frame);
188
189 /* Frame */
190 inside_frame = gtk_frame_new(NULL);
191 gtk_frame_set_shadow_type(GTK_FRAME(inside_frame), GTK_SHADOW_IN);
192 gtk_container_set_border_width(GTK_CONTAINER(inside_frame), 5);
193 gtk_container_add(GTK_CONTAINER(outside_frame), inside_frame);
194
195 /* Progress bar */
196 progress_bar = gtk_progress_bar_new();
197 gtk_progress_bar_pulse(GTK_PROGRESS_BAR (progress_bar));
198 /* Make uniform pulsing */
199 gint pulse_ref = g_timeout_add(300, pulse_bar, progress_bar);
200 g_object_set_data(G_OBJECT(progress_bar), "pulse_id",
201 GINT_TO_POINTER(pulse_ref));
202 gtk_container_add(GTK_CONTAINER(inside_frame), progress_bar);
203
204 gtk_widget_show_all(top_window);
205 printf("gtk_widget_show_all\n");
206
207 g_signal_connect(G_OBJECT (top_window), "delete-event",
208 G_CALLBACK(cb_delete), NULL);
209
210 if(!g_thread_create(&create_thread, progress_bar, FALSE, NULL) != 0)
211 g_warning("cannot create the thread");
212
213 gtk_main();
214 gdk_threads_leave();
215 printf("gdk_threads_leave\n");
216
217 return 0;
218 }
219