Add functions for drawing text, try to do so (not very successful yet)
[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         pqxx::work t(conn, "trx");
162
163         // fetch all songs
164         pqxx::result res( t.exec("SELECT * FROM songs") );
165         unsigned y = 0;
166         for (pqxx::result::const_iterator i = res.begin(); i != res.end(); ++i) {
167                 my_draw_text(i["title"].as(widestring()), framebuf, 0, y, 1, 255, 255, 255, font);
168                 y += 20;
169                 std::fprintf(stderr, "%s\n", i["title"].c_str());
170         }
171         t.commit();
172
173         ptc_update(framebuf);
174         sleep(1);
175 }
176
177 void init_freetype()
178 {
179         FT_Library library;
180         if (FT_Init_FreeType(&library))
181                 throw std::logic_error("FreeType init failed.");
182         if (FT_New_Face(library, "/usr/share/fonts/truetype/msttcorefonts/Georgia.ttf", 0, &font))
183                 throw std::logic_error("Face opening failed.");
184         if (FT_Set_Char_Size(font, 0, 12 * 64, 96, 96))
185                 throw std::logic_error("Size set failed.");
186 }
187
188 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)
189 {
190         FT_GlyphSlot slot = face->glyph;
191         int x = 0;
192
193         for (widestring::const_iterator i = str.begin(); i != str.end(); ++i) { 
194                 int glyph_index = FT_Get_Char_Index(face, *i);
195                 if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER))
196                         continue;
197
198                 if (real_render) {
199                         int y;
200                         FT_Bitmap *bm = &(slot->bitmap);
201                         for (y = 0; y < bm->rows; y++) {
202                                 int xx;
203                                 int dsty = ypos - slot->bitmap_top + y;
204                                 if (dsty < 0 || dsty > 599) continue;
205
206                                 unsigned char *dst = buf + dsty * 800*4 + (x + xpos + slot->bitmap_left)*4;
207                                 unsigned char *src = bm->buffer + y * bm->width;
208                                 for (xx = 0; xx < bm->width; xx++) {
209                                         *dst = (*dst * (256-*src) + r * *src) >> 8;
210                                         *dst++;
211                                         *dst = (*dst * (256-*src) + g * *src) >> 8;
212                                         *dst++;
213                                         *dst = (*dst * (256-*src) + b * *src) >> 8;
214                                         *dst++;
215                                         *dst++ = 0;
216                                         src++;
217                                 }
218                         }
219                 }
220
221                 x += slot->advance.x >> 6;
222         }
223
224         return x;
225 }
226
227
228 int main(int argc, char **argv)
229 {
230         ucs4_iconv = iconv_open("ucs-4", "utf-8");
231         
232         ptc_open("CCBS bigscreen", 800, 600);
233         
234         try {
235                 init_freetype();
236                 pqxx::connection conn("dbname=ccbs host=altersex.samfundet.no user=ccbs password=GeT|>>B_");
237                 FlagTrigger tournament_changed(conn, "active_tournament");
238                 
239                 // when active_tournament is changed, we destroy everything and start from scratch
240                 for ( ;; ) {
241                         tournament_changed.reset_flag();
242                         init(conn);
243                         do {
244                                 main_loop(conn);
245                                 conn.get_notifs();
246                         } while (!tournament_changed.get_flag());
247                         std::fprintf(stderr, "active_tournament changed, resetting...\n");
248                 }
249         } catch (const std::exception &e) {
250                 std::fprintf(stderr, "Exception: %s\n", e.what());
251                 exit(1);
252         }
253         
254         return 0;
255 }