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