make UI tips context-dependent
[cycon.git] / main.c
blob1a5f356c4c2b358d1290e75bfcf814587b9df1a8
1 /*
2 * Copyright (c) 2019, De Rais <derais@cock.li>
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
19 #include <locale.h>
20 #include <stdio.h>
21 #include <unistd.h>
23 #include <libwebsockets.h>
24 #include <pthread.h>
26 #include "cycon.h"
27 #include "macros.h"
29 const char *program_name = "cycon";
31 /* Usage message */
32 static void
33 usage(void)
35 int len = strlen(program_name) + 7;
37 fprintf(stderr, "usage: %s [ -s SERVER ] # like \"cytu.be\"\n",
38 program_name);
39 fprintf(stderr, "%*s [ -h ] # if website is http (not https)\n",
40 len, "");
41 fprintf(stderr, "%*s -c CHANNEL\n", len, "");
44 /* LWS handling thread */
45 extern int cytube_lws_handler(struct lws *wsi, enum lws_callback_reasons reason,
46 void *user, void *in, size_t len);
47 void *
48 lws_spin(void *arg)
50 int lret = 0;
51 time_t now = 0;
52 struct state volatile *s = (struct state volatile *) arg;
53 char *ripped_host = 0;
54 const struct lws_protocols p[] = {
55 /* */
57 /* */
58 .name = "sync", /* */
59 .callback = cytube_lws_handler, /* */
60 .per_session_data_size = sizeof(struct state), /* */
61 .rx_buffer_size = 0, /* */
62 .id = 0, /* */
63 .user = (void *) s, /* */
64 .tx_packet_size = 0, /* */
65 }, { 0 },
67 struct lws_context_creation_info info = {
68 /* */
69 .options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT, /* */
70 .port = CONTEXT_PORT_NO_LISTEN, /* */
71 .protocols = p, /* */
73 struct lws_context *context = 0;
74 struct lws *wsi = 0;
75 int sslf = s->https ? LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED : 0;
76 struct lws_client_connect_info cci = {
77 /* */
78 .context = 0, /* */
79 .ssl_connection = sslf, /* */
80 .address = s->address, /* */
81 .origin = s->address, /* */
82 .protocol = "sync", /* */
83 .userdata = (void *) &s, /* */
84 .pwsi = &wsi, /* */
87 /* First, HTTP stuff in preparation for LWS */
88 if (cytube_get_session_cookie(cci.address, (s->https ? "https" :
89 "http"), s->channel_name,
90 s) < 0) {
91 ERROR_MESSAGE("cannot extract ip-session cookie");
92 ERROR_MESSAGE("perhaps \"%s\" is not a channel?",
93 s->channel_name);
94 s->please_die = 1;
95 pthread_exit(0);
98 /* See cb_get_real_server */
99 if (s->https) {
100 ripped_host = strdup(s->socket_host + 8);
101 } else {
102 ripped_host = strdup(s->socket_host + 7);
105 if (!(ripped_host)) {
106 goto done;
109 for (char *p = ripped_host; *p; ++p) {
110 if (*p == ':') {
111 *p = 0;
112 cci.port = strtoll(p + 1, 0, 0);
113 break;
117 cci.host = ripped_host;
118 cci.address = ripped_host;
119 lws_set_log_level(0, 0);
120 cytube_set_cci_and_s(&cci, s);
122 if (!(context = lws_create_context(&info))) {
123 ERROR_MESSAGE("lws_create_context failed");
124 s->please_die = 1;
125 pthread_exit(0);
128 cci.context = context;
129 s->last_playlist_req = time(0) + 120;
131 while (!s->please_die) {
132 if ((lret = lws_service(context, 1000)) < 0) {
133 break;
136 /* Do we need to ping? */
137 now = time(0);
139 if (s->established) {
140 if (s->last_ping + (s->ping_interval / 1000) <= now) {
141 s->must_write_ping = 1;
142 s->last_ping = now;
145 if (s->last_playlist_req + 120 <= now) {
146 s->must_ask_for_playlist = 1;
147 s->last_playlist_req = now;
151 /* Tell LWS we want to write */
152 if (s->must_write_upgrade ||
153 s->must_write_ping ||
154 s->must_join_channel ||
155 s->must_ask_for_playlist) {
156 lws_callback_on_writable(wsi);
160 lws_context_destroy(context);
161 done:
162 free(ripped_host);
164 return 0;
167 /* Main method */
169 main(int argc, char **argv)
171 int ret = -1;
172 struct state s = {
173 /* */
174 .address = "cytu.be", .https = 1,
176 char *server_arg = 0;
177 char *channel_arg = 0;
178 int opt = 0;
179 pthread_t lws_thread = { 0 };
181 setlocale(LC_ALL, "");
183 /* Parse arguments */
184 while ((opt = getopt(argc, argv, "s:c:h")) != -1) {
185 switch (opt) {
186 case 's':
187 server_arg = optarg;
188 break;
189 case 'c':
190 channel_arg = optarg;
191 break;
192 case 'h':
193 s.https = 0;
194 break;
195 default:
196 usage();
198 return -1;
202 if (server_arg) {
203 s.address = server_arg;
206 if (!channel_arg) {
207 usage();
208 goto done;
211 s.channel_name = channel_arg;
212 s.playlist.current_playing_uid = -1;
214 if (ui_init(&s) < 0) {
215 PERROR_MESSAGE("ui initialization error");
216 goto done;
219 if (pthread_create(&lws_thread, 0, lws_spin, (void *) &s)) {
220 PERROR_MESSAGE("pthread_create");
221 goto done;
224 ui_loop(&s);
225 s.please_die = 1;
226 ret = 0;
227 pthread_join(lws_thread, 0);
228 done:
229 state_clean(&s);
231 return ret;