Subversion Repositories gelsvn

Rev

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

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