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