Move FlagTrigger into its own source file.
[ccbs] / bigscreen / ccbs_bigscreen.cpp
1 #include <cstdio>
2 #include <cstring>
3 #include <iconv.h>
4 #include <unistd.h>
5 #include <pqxx/pqxx>
6 #include <ft2build.h>
7 #include FT_FREETYPE_H
8 #include <tinyptc.h>
9 #include <endian.h>
10 #include "flagtrigger.h"
11
12 iconv_t ucs4_iconv;
13
14 // UCS-4 string with support for getting from UTF-8
15 class widestring : public std::wstring
16 {
17 public:
18         void operator= (const char *from)
19         {
20                 unsigned bytes = std::strlen(from);
21                 char *from_buf = strdup(from);
22                 wchar_t *to_buf = new wchar_t[bytes + 1];
23
24                 char *inptr = from_buf, *outptr = reinterpret_cast<char *> (to_buf);
25
26                 size_t in_left = bytes;
27                 size_t out_left = bytes * sizeof(wchar_t);
28
29                 size_t ret = iconv(ucs4_iconv, NULL, NULL, &outptr, &out_left);
30                 if (ret == (size_t)(-1)) {
31                         throw std::runtime_error("Error in iconv during initialization");
32                 }
33
34                 ret = iconv(ucs4_iconv, &inptr, &in_left, &outptr, &out_left);
35                 if (ret == (size_t)(-1)) {
36                         perror("iconv");
37                         throw std::runtime_error("Error in iconv during conversion");
38                 }
39
40                 erase(begin(), end());
41                 std::copy(to_buf, reinterpret_cast<wchar_t *> (outptr), std::back_inserter(*this));
42
43                 free(from_buf);
44                 delete[] to_buf;
45         }
46 };
47
48 template<>
49 void pqxx::from_string<widestring>(const char *from, widestring &to)
50 {
51         to = from;
52 }
53
54 int my_draw_text(const widestring &str, unsigned char *buf, int xpos, int ypos, bool real_render, int r, int g, int b, std::vector<FT_Face> &fontlist);
55
56 class Tournament {
57 public:
58         int id;
59         widestring name;
60 };
61
62 Tournament active_tournament;
63 std::vector<FT_Face> fonts;
64
65 /* A transactor that fetches the current tournament and some information about it. */
66 class FetchCurrentTournament : public pqxx::transactor<> {
67 private:
68         Tournament *tourn;
69
70 public:
71         FetchCurrentTournament(Tournament *tourn) : tourn(tourn) {}
72         void operator() (pqxx::transaction<> &t)
73         {
74                 pqxx::result res( t.exec("SELECT * FROM bigscreen.active_tournament NATURAL JOIN tournaments") );
75                 try {
76                         pqxx::result::tuple tournament = res.at(0);
77
78                         tourn->id = tournament["tournament"].as(tourn->id);
79                         tourn->name = tournament["tournamentname"].as(tourn->name);
80                 } catch (PGSTD::out_of_range &e) {
81                         tourn->id = -1;
82                         tourn->name = "";
83                 }
84         }
85 };
86
87 void init(pqxx::connection &conn)
88 {
89         conn.perform(FetchCurrentTournament(&active_tournament));
90
91         if (active_tournament.id == -1) {
92                 std::fprintf(stderr, "No active tournament\n");
93         } else {
94                 std::fprintf(stderr, "Current tournament is %d (name: '%s')\n",
95                         active_tournament.id, active_tournament.name.c_str());
96         }
97 }
98
99 unsigned char framebuf[800 * 600 * 4];
100
101 void main_loop(pqxx::connection &conn)
102 {
103         if (active_tournament.id == -1) {
104                 // No active tournament, sleep a second or so and exit
105                 sleep(1);
106                 return;
107         }
108
109         memset(framebuf, 0, 800*600*4);
110         
111         pqxx::work t(conn, "trx");
112
113         // fetch all songs
114         pqxx::result res( t.exec("SELECT * FROM songs WHERE title LIKE 'M%'") );
115         unsigned y = 0;
116         for (pqxx::result::const_iterator i = res.begin(); i != res.end(); ++i) {
117                 my_draw_text(i["title"].as(widestring()), framebuf, 0, y, 1, 255, 255, 255, fonts);
118                 y += 20;
119 //              std::fprintf(stderr, "%s\n", i["title"].c_str());
120         }
121         t.commit();
122
123         ptc_update(framebuf);
124         sleep(1);
125 }
126
127 void init_freetype()
128 {
129         FT_Library library;
130         FT_Face face;
131         if (FT_Init_FreeType(&library))
132                 throw std::runtime_error("FreeType init failed.");
133
134         // Georgia
135         if (FT_New_Face(library, "/usr/share/fonts/truetype/msttcorefonts/Georgia.ttf", 0, &face))
136                 throw std::runtime_error("Face opening failed.");
137         if (FT_Set_Char_Size(face, 0, 12 * 64, 96, 96))
138                 throw std::runtime_error("Size set failed.");
139         fonts.push_back(face);
140
141         // FreeSerif
142         if (FT_New_Face(library, "/usr/share/fonts/truetype/freefont/FreeSerif.ttf", 0, &face)) {
143                 std::fprintf(stderr, "Warning: Couldn't open FreeSerif, some glyphs might not be available\n");
144         } else {
145                 if (FT_Set_Char_Size(face, 0, 12 * 64, 96, 96))
146                         throw std::runtime_error("Size set failed.");
147                 fonts.push_back(face);
148         }
149         
150         // Arial Unicode MS
151         if (FT_New_Face(library, "arialuni.ttf", 0, &face)) {
152                 std::fprintf(stderr, "Warning: Couldn't open Arial Unicode MS, some glyphs might not be available\n");
153         } else {
154                 if (FT_Set_Char_Size(face, 0, 12 * 64, 96, 96))
155                         throw std::runtime_error("Size set failed.");
156                 fonts.push_back(face);
157         }
158 }
159
160 int my_draw_text(const widestring &str, unsigned char *buf, int xpos, int ypos, bool real_render, int r, int g, int b, std::vector<FT_Face> &fontlist)
161 {
162         FT_GlyphSlot slot;
163         int x = 0;
164
165         for (widestring::const_iterator i = str.begin(); i != str.end(); ++i) {
166                 int glyph_index;
167                 for (std::vector<FT_Face>::const_iterator j = fontlist.begin(); j != fontlist.end(); ++j) {
168                         glyph_index = FT_Get_Char_Index(*j, *i);
169                         if (glyph_index == 0)
170                                 continue;
171
172                         if (FT_Load_Glyph(*j, glyph_index, FT_LOAD_RENDER))
173                                 throw std::runtime_error("Couldn't load glyph");
174                         slot = (*j)->glyph;
175                         break;
176                 }
177                 if (glyph_index == 0) {
178                         std::fprintf(stderr, "Warning: Could not find a glyph in any font for U+%x, ignoring\n", *i);
179                         continue;
180                 }
181
182                 if (real_render) {
183                         int y;
184                         FT_Bitmap *bm = &(slot->bitmap);
185                         for (y = 0; y < bm->rows; y++) {
186                                 int xx;
187                                 int dsty = ypos - slot->bitmap_top + y;
188                                 if (dsty < 0 || dsty > 599) continue;
189
190                                 unsigned char *dst = buf + dsty * 800*4 + (x + xpos + slot->bitmap_left)*4;
191                                 unsigned char *src = bm->buffer + y * bm->width;
192                                 for (xx = 0; xx < bm->width; xx++) {
193                                         *dst = (*dst * (256-*src) + r * *src) >> 8;
194                                         *dst++;
195                                         *dst = (*dst * (256-*src) + g * *src) >> 8;
196                                         *dst++;
197                                         *dst = (*dst * (256-*src) + b * *src) >> 8;
198                                         *dst++;
199                                         *dst++ = 0;
200                                         src++;
201                                 }
202                         }
203                 }
204
205                 x += slot->advance.x >> 6;
206         }
207
208         return x;
209 }
210
211
212 int main(int argc, char **argv)
213 {
214 #if __BYTE_ORDER == __LITTLE_ENDIAN
215         ucs4_iconv = iconv_open("ucs-4le", "utf-8");
216 #else   
217         ucs4_iconv = iconv_open("ucs-4be", "utf-8");
218 #endif
219         
220         ptc_open("CCBS bigscreen", 800, 600);
221         
222         try {
223                 init_freetype();
224                 pqxx::connection conn("dbname=ccbs host=altersex.samfundet.no user=ccbs password=GeT|>>B_");
225                 FlagTrigger tournament_changed(conn, "active_tournament");
226                 FlagTrigger rounds_changed(conn, "active_groups");
227                 
228                 // when active_tournament or active_rounds is changed, we destroy everything and start from scratch
229                 // (at least currently)
230                 for ( ;; ) {
231                         tournament_changed.reset_flag();
232                         rounds_changed.reset_flag();
233                         init(conn);
234                         do {
235                                 main_loop(conn);
236                                 conn.get_notifs();
237                         } while (!tournament_changed.get_flag() && !rounds_changed.get_flag());
238                         std::fprintf(stderr, "active_tournament or active_groups changed, resetting...\n");
239                 }
240         } catch (const std::exception &e) {
241                 std::fprintf(stderr, "Exception: %s\n", e.what());
242                 exit(1);
243         }
244         
245         return 0;
246 }