Move widestring into its own 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 "flagtrigger.h"
10 #include "widestring.h"
11
12 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);
13
14 class Tournament {
15 public:
16         int id;
17         widestring name;
18 };
19
20 Tournament active_tournament;
21 std::vector<FT_Face> fonts;
22
23 /* A transactor that fetches the current tournament and some information about it. */
24 class FetchCurrentTournament : public pqxx::transactor<> {
25 private:
26         Tournament *tourn;
27
28 public:
29         FetchCurrentTournament(Tournament *tourn) : tourn(tourn) {}
30         void operator() (pqxx::transaction<> &t)
31         {
32                 pqxx::result res( t.exec("SELECT * FROM bigscreen.active_tournament NATURAL JOIN tournaments") );
33                 try {
34                         pqxx::result::tuple tournament = res.at(0);
35
36                         tourn->id = tournament["tournament"].as(tourn->id);
37                         tourn->name = tournament["tournamentname"].as(tourn->name);
38                 } catch (PGSTD::out_of_range &e) {
39                         tourn->id = -1;
40                         tourn->name = "";
41                 }
42         }
43 };
44
45 void init(pqxx::connection &conn)
46 {
47         conn.perform(FetchCurrentTournament(&active_tournament));
48
49         if (active_tournament.id == -1) {
50                 std::fprintf(stderr, "No active tournament\n");
51         } else {
52                 std::fprintf(stderr, "Current tournament is %d (name: '%s')\n",
53                         active_tournament.id, active_tournament.name.c_str());
54         }
55 }
56
57 unsigned char framebuf[800 * 600 * 4];
58
59 void main_loop(pqxx::connection &conn)
60 {
61         if (active_tournament.id == -1) {
62                 // No active tournament, sleep a second or so and exit
63                 sleep(1);
64                 return;
65         }
66
67         memset(framebuf, 0, 800*600*4);
68         
69         pqxx::work t(conn, "trx");
70
71         // fetch all songs
72         pqxx::result res( t.exec("SELECT * FROM songs WHERE title LIKE 'M%'") );
73         unsigned y = 0;
74         for (pqxx::result::const_iterator i = res.begin(); i != res.end(); ++i) {
75                 my_draw_text(i["title"].as(widestring()), framebuf, 0, y, 1, 255, 255, 255, fonts);
76                 y += 20;
77 //              std::fprintf(stderr, "%s\n", i["title"].c_str());
78         }
79         t.commit();
80
81         ptc_update(framebuf);
82         sleep(1);
83 }
84
85 void init_freetype()
86 {
87         FT_Library library;
88         FT_Face face;
89         if (FT_Init_FreeType(&library))
90                 throw std::runtime_error("FreeType init failed.");
91
92         // Georgia
93         if (FT_New_Face(library, "/usr/share/fonts/truetype/msttcorefonts/Georgia.ttf", 0, &face))
94                 throw std::runtime_error("Face opening failed.");
95         if (FT_Set_Char_Size(face, 0, 12 * 64, 96, 96))
96                 throw std::runtime_error("Size set failed.");
97         fonts.push_back(face);
98
99         // FreeSerif
100         if (FT_New_Face(library, "/usr/share/fonts/truetype/freefont/FreeSerif.ttf", 0, &face)) {
101                 std::fprintf(stderr, "Warning: Couldn't open FreeSerif, some glyphs might not be available\n");
102         } else {
103                 if (FT_Set_Char_Size(face, 0, 12 * 64, 96, 96))
104                         throw std::runtime_error("Size set failed.");
105                 fonts.push_back(face);
106         }
107         
108         // Arial Unicode MS
109         if (FT_New_Face(library, "arialuni.ttf", 0, &face)) {
110                 std::fprintf(stderr, "Warning: Couldn't open Arial Unicode MS, some glyphs might not be available\n");
111         } else {
112                 if (FT_Set_Char_Size(face, 0, 12 * 64, 96, 96))
113                         throw std::runtime_error("Size set failed.");
114                 fonts.push_back(face);
115         }
116 }
117
118 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)
119 {
120         FT_GlyphSlot slot;
121         int x = 0;
122
123         for (widestring::const_iterator i = str.begin(); i != str.end(); ++i) {
124                 int glyph_index;
125                 for (std::vector<FT_Face>::const_iterator j = fontlist.begin(); j != fontlist.end(); ++j) {
126                         glyph_index = FT_Get_Char_Index(*j, *i);
127                         if (glyph_index == 0)
128                                 continue;
129
130                         if (FT_Load_Glyph(*j, glyph_index, FT_LOAD_RENDER))
131                                 throw std::runtime_error("Couldn't load glyph");
132                         slot = (*j)->glyph;
133                         break;
134                 }
135                 if (glyph_index == 0) {
136                         std::fprintf(stderr, "Warning: Could not find a glyph in any font for U+%x, ignoring\n", *i);
137                         continue;
138                 }
139
140                 if (real_render) {
141                         int y;
142                         FT_Bitmap *bm = &(slot->bitmap);
143                         for (y = 0; y < bm->rows; y++) {
144                                 int xx;
145                                 int dsty = ypos - slot->bitmap_top + y;
146                                 if (dsty < 0 || dsty > 599) continue;
147
148                                 unsigned char *dst = buf + dsty * 800*4 + (x + xpos + slot->bitmap_left)*4;
149                                 unsigned char *src = bm->buffer + y * bm->width;
150                                 for (xx = 0; xx < bm->width; xx++) {
151                                         *dst = (*dst * (256-*src) + r * *src) >> 8;
152                                         *dst++;
153                                         *dst = (*dst * (256-*src) + g * *src) >> 8;
154                                         *dst++;
155                                         *dst = (*dst * (256-*src) + b * *src) >> 8;
156                                         *dst++;
157                                         *dst++ = 0;
158                                         src++;
159                                 }
160                         }
161                 }
162
163                 x += slot->advance.x >> 6;
164         }
165
166         return x;
167 }
168
169
170 int main(int argc, char **argv)
171 {
172         ptc_open("CCBS bigscreen", 800, 600);
173         
174         try {
175                 init_freetype();
176                 pqxx::connection conn("dbname=ccbs host=altersex.samfundet.no user=ccbs password=GeT|>>B_");
177                 FlagTrigger tournament_changed(conn, "active_tournament");
178                 FlagTrigger rounds_changed(conn, "active_groups");
179                 
180                 // when active_tournament or active_rounds is changed, we destroy everything and start from scratch
181                 // (at least currently)
182                 for ( ;; ) {
183                         tournament_changed.reset_flag();
184                         rounds_changed.reset_flag();
185                         init(conn);
186                         do {
187                                 main_loop(conn);
188                                 conn.get_notifs();
189                         } while (!tournament_changed.get_flag() && !rounds_changed.get_flag());
190                         std::fprintf(stderr, "active_tournament or active_groups changed, resetting...\n");
191                 }
192         } catch (const std::exception &e) {
193                 std::fprintf(stderr, "Exception: %s\n", e.what());
194                 exit(1);
195         }
196         
197         return 0;
198 }