Subversion Repositories gelsvn

Rev

Rev 561 | Rev 597 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
561 awk 1
/**
2
 * @file   Console.h
3
 * @author Anders Wang Kristensen <awk@imm.dtu.dk>
4
 * @date   Fri Oct 22 18:32:06 2011
5
 *
6
 * @brief  OpenGL 'Quake'-like console
7
 */
8
 
9
#ifndef __GEL_GLGRAPHICS_CONSOLE_H__
10
#define __GEL_GLGRAPHICS_CONSOLE_H__
11
 
12
#include <GLGraphics/gel_gl.h>
13
 
14
#include <functional> //make_shared, bind
15
#include <map> //multimap
16
#include <sstream> //stringstream
17
#include <vector>
18
#include <cassert>
19
 
20
namespace GLGraphics {
21
 
22
class Console
23
{
24
public:
25
    Console();
26
    ~Console() throw();
27
 
28
    //GLUT functions
29
    void display();
30
    void keyboard(unsigned char key);
31
    void special(int key);
32
 
33
    //stdio-like io
34
    void print(const char* buffer);
35
    void printf(const char* format, ...);
36
    void newline();
37
 
38
    //execute command
39
    void execute(const char* buffer);
40
    void executef(const char* format, ...);
41
 
42
    typedef int cmd_token;
43
 
44
    //0-ary
563 awk 45
    inline cmd_token reg_cmd0(const std::string& name,
46
                              const std::function<void ()>& f,
47
                              const std::string& help);
561 awk 48
 
49
    //1-ary
50
    template <typename A0>
563 awk 51
    cmd_token reg_cmd1(const std::string& name,
52
                       const std::function<void (const A0&)>& f,
53
                       const std::string& help);
561 awk 54
 
55
    //2-ary
56
    template <typename A0, typename A1>
563 awk 57
    cmd_token reg_cmd2(const std::string& name,
58
                       const std::function<void (const A0&,const A1&)>& f,
59
                       const std::string& help);
561 awk 60
 
61
    //3-ary
62
    template <typename A0, typename A1, typename A2>
563 awk 63
    cmd_token reg_cmd3(const std::string& name,
64
                       const std::function<void (const A0&,
65
                                                 const A1&,const A2&)>& f,
66
                       const std::string& help);
561 awk 67
 
563 awk 68
    //N-ary
69
    inline cmd_token reg_cmdN(const std::string& name,
70
                              const std::function<
71
                                void (const std::vector<std::string>&)>& f,
72
                              const std::string& help);
73
 
561 awk 74
    //remove
75
    inline void unreg_cmd(cmd_token);
76
 
77
    //get name/help of registrered command
78
    const char* get_name(cmd_token) const;
79
    const char* get_help(cmd_token) const;
80
 
81
    //helper classes
82
    class command;
83
 
84
    template <typename T>
85
    class variable;
86
 
87
private:
88
    //make noncopyable
89
    Console(Console&);
90
    const Console& operator=(const Console&);
91
 
92
    static inline void from_string(const std::string& source,
93
                                   std::string& target)
94
    {
95
        target = source;
96
    }
97
 
98
    template <typename T>
99
    static inline void from_string(const std::string& source, T& target)
100
    {
101
        std::stringstream ss(source);
102
        if (!(ss >> target))
103
        {
104
            std::stringstream ss;
105
            ss << "Cannot convert "
106
               << source << " to '" << typeid(T).name() << "'.";
107
            throw std::invalid_argument(ss.str());
108
        }
109
    }
110
 
111
    //from string
112
    template <typename T>
113
    static T lexical_cast(const std::string& source)
114
    {
115
        T target;
116
        from_string(source, target);
117
        return target;
118
    }
119
 
120
    //to string
121
    template <typename S>
122
    static std::string to_string(const S& source)
123
    {
124
        std::stringstream ss;
125
        if (!(ss << source))
126
        {
127
            //converting *to* string should never fail
128
            throw std::invalid_argument("Cannot convert argument to string.");
129
        }
130
 
131
        return ss.str();
132
    }
133
 
134
    class command_base
135
    {
136
    public:
137
        inline command_base(Console& c, const std::string& help)
138
            : m_console(c), m_help(help) { m_id = c.m_id_counter++; }
139
        virtual ~command_base() {}
140
 
141
        virtual void execute(const std::vector<std::string>& args) const = 0;
142
        virtual size_t arity() const = 0;
143
 
144
        inline cmd_token get_id() const { return m_id; }
145
 
146
        inline const char* get_help() const { return m_help.c_str(); }
147
 
148
    protected:
149
        Console& m_console;
150
        cmd_token m_id;
151
        std::string m_help;
152
    };
153
 
154
    //no need to be a template
155
    class command0 : public command_base
156
    {
157
    public:
158
        typedef std::function<void ()> function_type;
159
 
160
        inline command0(Console& c,
161
                        const std::string& h, const function_type& f)
162
            : command_base(c,h), m_callback(f) {}
163
 
164
        inline void execute(const std::vector<std::string>& args) const;
165
        inline size_t arity() const { return 0; }
166
 
167
    private:
168
        function_type m_callback;
169
    };
170
 
171
    template <typename A0>
172
    class command1 : public command_base
173
    {
174
    public:
175
        typedef typename std::function<void (const A0&)> function_type;
176
 
177
        command1(Console& c, const std::string& h, const function_type& f)
178
            : command_base(c,h), m_callback(f) {}
179
 
180
        void execute(const std::vector<std::string>& args) const;
181
        size_t arity() const { return 1; }
182
 
183
    private:
184
        function_type m_callback;
185
    };
186
 
187
    template <typename A0, typename A1>
188
    class command2 : public command_base
189
    {
190
    public:
191
        typedef typename std::function<void (const A0&,
192
                                             const A1&)> function_type;
193
 
194
        command2(Console& c, const std::string& h, const function_type& f)
195
            : command_base(c,h), m_callback(f) {}
196
 
197
        void execute(const std::vector<std::string>& args) const;
198
        size_t arity() const { return 2; }
199
 
200
    private:
201
        function_type m_callback;
202
    };
203
 
204
    template <typename A0, typename A1, typename A2>
205
    class command3 : public command_base
206
    {
207
    public:
208
        typedef typename std::function<void (const A0&,
209
                                             const A1&,
210
                                             const A2&)> function_type;
211
 
212
        command3(Console& c, const std::string& h, const function_type& f)
213
            : command_base(c,h), m_callback(f) {}
214
 
215
        void execute(const std::vector<std::string>& args) const;
216
        size_t arity() const { return 3; }
217
 
218
    private:
219
        function_type m_callback;
220
    };
221
 
563 awk 222
    //no need to be a template
223
    class commandN : public command_base
224
    {
225
    public:
226
        typedef std::function<
227
            void (const std::vector<std::string>&)> function_type;
228
 
229
        inline commandN(Console& c,
230
                        const std::string& h, const function_type& f)
231
            : command_base(c,h), m_callback(f) {}
232
 
233
        inline void execute(const std::vector<std::string>& args) const;
234
        inline size_t arity() const { return any_arity; }
235
 
236
    private:
237
        function_type m_callback;
238
    };
239
 
561 awk 240
    //multi, so we can have multiple cmds with same name (but different arity)
241
    typedef std::multimap<std::string,
242
                          std::unique_ptr<command_base> > command_map_t;
243
 
244
    cmd_token add_command(const std::string& name,
245
                           std::unique_ptr<command_base>&& ptr);
246
    void remove_command(cmd_token);
247
 
248
    void tab_completion();
249
 
250
    std::vector<std::string> parse_cmdline(const char* buffer) const;
251
 
252
    //builtin commands
253
    void help();
254
    void help(const std::string&);
255
    void clear();
256
    void history();
257
 
258
    void load_history();
259
    void save_history() const;
260
    void clear_history();
261
 
563 awk 262
    enum { any_arity = 0xFFFF };
263
 
561 awk 264
    //draw commands
265
    void draw_text(int x, int y,
266
                   float r, float g, float b,
267
                   const char* buffer);
268
 
269
    void draw_textf(int x, int y,
270
                    float r, float g, float b,
271
                    const char* fmt, ...);
272
    //state
273
    command_map_t m_commands;
274
    std::vector<std::string> m_buffer;
275
 
276
    size_t m_history_index;
277
    std::vector<std::string> m_history;
278
 
279
    std::string::size_type m_caret;
280
    std::string m_current_command;
281
 
282
    int m_id_counter;
283
    bool m_is_executing;
284
 
285
    GLuint m_font;
286
 
287
    static const unsigned char g_png_data[];
288
    static const size_t g_png_size;
289
};
290
 
291
//0-ary
563 awk 292
Console::cmd_token Console::reg_cmd0(const std::string& name,
293
                                     const std::function<void ()>& f,
294
                                     const std::string& help)
561 awk 295
{
296
    typedef command0 command_type;
297
    return add_command(name,
298
        std::unique_ptr<command_type>(
299
            new command_type(std::ref(*this), help, f)));
300
}
301
 
302
void Console::command0::execute(const std::vector<std::string>& args) const
303
{
304
    assert(args.size() == arity());
305
    m_callback();
306
}
307
 
308
//1-ary
309
template <typename A0>
563 awk 310
Console::cmd_token Console::reg_cmd1(const std::string& name,
311
                                     const std::function<void (const A0&)>& f,
312
                                     const std::string& help)
561 awk 313
{
314
    typedef command1<A0> command_type;
315
    return add_command(name,
316
        std::unique_ptr<command_type>(
317
            new command_type(std::ref(*this), help, f)));
318
}
319
 
320
template <typename A0>
321
void Console::command1<A0>::execute(const std::vector<std::string>& args) const
322
{
323
    assert(args.size() == arity());
324
    m_callback(lexical_cast<A0>(args[0]));
325
}
326
 
327
//2-ary
328
template <typename A0, typename A1>
563 awk 329
Console::cmd_token Console::reg_cmd2(const std::string& name,
330
                                     const std::function<void (const A0&,
331
                                       const A1&)>& f,
332
                                     const std::string& help)
561 awk 333
{
334
    typedef command2<A0,A1> command_type;
335
    return add_command(name,
336
        std::unique_ptr<command_type>(
337
            new command_type(std::ref(*this), help, f)));
338
}
339
 
340
template <typename A0,typename A1>
341
void Console::command2<A0,A1>::execute(
342
    const std::vector<std::string>& args) const
343
{
344
    assert(args.size() == arity());
345
    m_callback(lexical_cast<A0>(args[0]),
346
               lexical_cast<A1>(args[1]));
347
}
348
 
349
//3-ary
350
template <typename A0, typename A1, typename A2>
563 awk 351
Console::cmd_token Console::reg_cmd3(const std::string& name,
352
                                     const std::function<void (const A0&,
353
                                       const A1&,const A2&)>& f,
354
                                     const std::string& help)
561 awk 355
{
356
    typedef command3<A0,A1,A2> command_type;
357
    return add_command(name,
358
        std::unique_ptr<command_type>(
359
            new command_type(std::ref(*this), help, f)));
360
}
361
 
362
template <typename A0,typename A1, typename A2>
363
void Console::command3<A0,A1,A2>::execute(
364
    const std::vector<std::string>& args) const
365
{
366
    assert(args.size() == arity());
367
    m_callback(lexical_cast<A0>(args[0]),
368
               lexical_cast<A1>(args[1]),
369
               lexical_cast<A2>(args[2]));
370
}
371
 
563 awk 372
//N-ary
373
Console::cmd_token Console::reg_cmdN(const std::string& name,
374
    const std::function<void (const std::vector<std::string>&)>& f,
375
    const std::string& help)
376
{
377
    typedef commandN command_type;
378
    return add_command(name,
379
        std::unique_ptr<command_type>(
380
            new command_type(std::ref(*this), help, f)));
381
}
382
 
383
void Console::commandN::execute(const std::vector<std::string>& args) const
384
{
385
    m_callback(args);
386
}
387
 
561 awk 388
void Console::unreg_cmd(cmd_token id)
389
{
390
    remove_command(id);
391
}
392
 
393
class Console::command
394
{
395
public:
396
    inline command() : m_console(NULL) {}
397
 
398
    inline void reg(Console& cs,
399
        const std::string& name,
400
        const std::function<void ()>& function,
401
        const std::string& help)
402
    {
403
        assert(!m_console);
404
        m_console = &cs;
563 awk 405
        m_id = m_console->reg_cmd0(name, function, help);
561 awk 406
    }
407
 
408
    template <typename A0>
409
    void reg(Console& cs,
410
        const std::string& name,
411
        const std::function<void (const A0&)>& function,
412
        const std::string& help)
413
    {
414
        assert(!m_console);
415
        m_console = &cs;
563 awk 416
        m_id = m_console->reg_cmd1<A0>(name, function, help);
561 awk 417
    }
418
 
419
    template <typename A0, typename A1>
420
    void reg(Console& cs,
421
        const std::string& name,
422
        const std::function<void (const A0&, const A1&)>& function,
423
        const std::string& help)
424
    {
425
        assert(!m_console);
426
        m_console = &cs;
563 awk 427
        m_id = m_console->reg_cmd2<A0,A1>(name, function, help);
561 awk 428
    }
429
 
430
    template <typename A0, typename A1, typename A2>
431
    void reg(Console& cs,
432
        const std::string& name,
433
        const std::function<void (const A0&,
434
        const A1&, const A2&)>& function,
435
        const std::string& help)
436
    {
437
        assert(!m_console);
438
        m_console = &cs;
563 awk 439
        m_id = m_console->reg_cmd3<A0,A1,A2>(name, function, help);
561 awk 440
    }
441
 
442
    inline ~command()
443
    {
444
        if (m_console)
445
            m_console->unreg_cmd(m_id);
446
    }
447
 
448
    inline const char* get_name() const
449
    {
450
        assert(m_console);
451
        return m_console->get_name(m_id);
452
    }
453
 
454
    inline const char* get_help() const
455
    {
456
        assert(m_console);
457
        return m_console->get_help(m_id);
458
    }
459
 
460
    inline Console* get_console() const { return m_console; }
461
    inline cmd_token get_id() const { assert(m_console); return m_id; }
462
 
463
private:
464
    command(command&);
465
    const command& operator=(const command&);
466
 
467
    Console* m_console;
468
    cmd_token m_id;
469
};
470
 
471
template <typename T>
472
class Console::variable
473
{
474
public:
475
    variable(const T& initial_value = T())
476
        : m_value(initial_value) {}
477
 
478
    void reg(Console& cs,
479
        const std::string& name,
480
        const std::string& help)
481
    {
482
        m_print_cmd.reg(cs, name,
483
            std::bind(&variable::print_value, this), help);
484
 
485
        m_set_cmd.reg<T>(cs, name,
486
            std::bind(&variable::set_value, this, std::placeholders::_1),
487
            help);
488
    }
489
 
490
    const variable& operator=(const T& value) { m_value = value; return *this; }
491
 
492
    operator const T&() const { return m_value; }
493
 
494
    const char* get_name() const { return m_print_cmd.get_name(); }
495
    const char* get_help() const { return m_print_cmd.get_help(); }
496
 
497
private:
498
    variable(const variable&);
499
    const variable& operator=(const variable&);
500
 
501
    void print_value()
502
    {
503
        m_print_cmd.get_console()->printf("%s = %s",
504
            m_print_cmd.get_name(),
505
            Console::to_string(m_value).c_str());
506
    }
507
 
508
    void set_value(const T& value)
509
    {
510
        m_value = value;
511
        m_print_cmd.get_console()->execute(m_print_cmd.get_name());
512
    }
513
 
514
    T m_value;
515
 
516
    command m_print_cmd;
517
    command m_set_cmd;
518
};
519
 
520
}
521
 
522
#endif //__GEL_GLGRAPHICS_CONSOLE_H__