Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / misc / gtk_displayer.cpp
blobbc6741d7dae2db64179537a8fc1f366d27870aa6
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2015 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdmisc.h"
21 #include "nel/misc/gtk_displayer.h"
23 #ifdef NL_USE_GTK
25 #include <gtk/gtk.h>
26 #include <gdk/gdk.h>
27 //#include <gobject/gobject.h>
28 #include <gdk/gdkkeysyms.h>
30 #ifdef NL_OS_WINDOWS
31 // automatically add gtk library
32 #pragma comment(lib, "gtk-1.3.lib")
33 #pragma comment(lib, "gdk-1.3.lib")
34 #pragma comment(lib, "glib-1.3.lib")
35 #pragma comment(lib, "gthread-1.3.lib")
36 #endif
38 #include "nel/misc/app_context.h"
39 #include "nel/misc/path.h"
40 #include "nel/misc/command.h"
41 #include "nel/misc/thread.h"
43 using namespace std;
45 #ifdef DEBUG_NEW
46 #define new DEBUG_NEW
47 #endif
49 namespace NLMISC {
52 // Variables
55 static vector<string> CommandHistory;
56 static uint32 CommandHistoryPos = 0;
57 static CLog *Log = 0;
59 static GtkWidget *RootWindow = NULL, *OutputText = NULL, *InputText = NULL;
60 static GtkWidget *hrootbox = NULL, *scrolled_win2 = NULL;
63 // Functions
66 CGtkDisplayer (const char *displayerName) : CWindowDisplayer(displayerName)
68 needSlashR = false;
69 createLabel ("@Clear|CLEAR");
71 INelContext::getInstance().setWindowedApplication(true);
74 CGtkDisplayer::~CGtkDisplayer ()
76 if (_Init)
81 gint ButtonClicked(GtkWidget *Widget, gpointer *Data)
83 CGtkDisplayer *disp = (CGtkDisplayer *) Data;
85 // find the button and execute the command
86 CSynchronized<std::vector<CGtkDisplayer::CLabelEntry> >::CAccessor access (&(disp->_Labels));
87 for (uint i = 0; i < access.value().size(); i++)
89 if (access.value()[i].Hwnd == Widget)
91 if(access.value()[i].Value == "@Clear|CLEAR")
93 // special commands because the clear must be called by the display thread and not main thread
94 disp->clear ();
96 else
98 // the button was found, add the command in the command stack
99 CSynchronized<std::vector<std::string> >::CAccessor accessCommands (&disp->_CommandsToExecute);
100 string str;
101 nlassert (!access.value()[i].Value.empty());
102 nlassert (access.value()[i].Value[0] == '@');
104 string::size_type pos = access.value()[i].Value.find ("|");
105 if (pos != string::npos)
107 str = access.value()[i].Value.substr(pos+1);
109 else
111 str = access.value()[i].Value.substr(1);
113 if (!str.empty())
114 accessCommands.value().push_back(str);
116 break;
119 return TRUE;
123 void CGtkDisplayer::updateLabels ()
126 CSynchronized<std::vector<CLabelEntry> >::CAccessor access (&_Labels);
127 for (uint i = 0; i < access.value().size(); i++)
129 if (access.value()[i].NeedUpdate && !access.value()[i].Value.empty())
131 string n;
133 if (access.value()[i].Value[0] != '@')
134 n = access.value()[i].Value;
135 else
137 string::size_type pos = access.value()[i].Value.find ('|');
138 if (pos != string::npos)
140 n = access.value()[i].Value.substr (1, pos - 1);
142 else
144 n = access.value()[i].Value.substr (1);
148 if (access.value()[i].Hwnd == NULL)
150 // create a button for command and label for variables
151 if (access.value()[i].Value[0] == '@')
153 access.value()[i].Hwnd = gtk_button_new_with_label (n.c_str());
154 nlassert (access.value()[i].Hwnd != NULL);
155 gtk_signal_connect (GTK_OBJECT (access.value()[i].Hwnd), "clicked", GTK_SIGNAL_FUNC (ButtonClicked), (gpointer) this);
156 GtkLabel *label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(access.value()[i].Hwnd)));
157 gtk_label_set_justify (label, GTK_JUSTIFY_LEFT);
158 gtk_label_set_line_wrap (label, FALSE);
159 gtk_widget_show (GTK_WIDGET (access.value()[i].Hwnd));
160 gtk_box_pack_start (GTK_BOX (hrootbox), GTK_WIDGET (access.value()[i].Hwnd), TRUE, TRUE, 0);
162 else
164 access.value()[i].Hwnd = gtk_label_new ("");
165 gtk_label_set_justify (GTK_LABEL (access.value()[i].Hwnd), GTK_JUSTIFY_LEFT);
166 gtk_label_set_line_wrap (GTK_LABEL (access.value()[i].Hwnd), FALSE);
167 gtk_widget_show (GTK_WIDGET (access.value()[i].Hwnd));
168 gtk_box_pack_start (GTK_BOX (hrootbox), GTK_WIDGET (access.value()[i].Hwnd), TRUE, TRUE, 0);
172 if (access.value()[i].Value[0] != '@')
173 gtk_label_set_text (GTK_LABEL (access.value()[i].Hwnd), n.c_str());
175 access.value()[i].NeedUpdate = false;
181 // windows delete event => quit
182 gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
184 gtk_main_quit();
186 exit(1);
187 return FALSE;
190 gint KeyIn(GtkWidget *Widget, GdkEventKey *Event, gpointer *Data)
192 switch (Event->keyval)
194 case GDK_Escape :
195 gtk_entry_set_text (GTK_ENTRY(Widget), "");
196 break;
197 case GDK_Up :
198 if (CommandHistoryPos > 0) {
199 CommandHistoryPos--;
200 gtk_entry_set_text (GTK_ENTRY(Widget), CommandHistory[CommandHistoryPos].c_str());
202 break;
203 case GDK_Down :
204 if (CommandHistoryPos + 1 < CommandHistory.size())
206 CommandHistoryPos++;
207 gtk_entry_set_text (GTK_ENTRY(Widget), CommandHistory[CommandHistoryPos].c_str());
209 break;
210 case GDK_KP_Enter :
211 gtk_signal_emit_by_name(GTK_OBJECT(Widget),"activate");
212 default :
213 return FALSE;
215 gtk_signal_emit_stop_by_name(GTK_OBJECT(Widget),"key_press_event");
216 return TRUE;
219 void updateInput ()
221 gtk_widget_grab_focus (InputText);
224 gint KeyOut(GtkWidget *Widget, GdkEventKey *Event, gpointer *Data)
226 updateInput();
227 gtk_signal_emit_stop_by_name(GTK_OBJECT(Widget),"key_press_event");
228 return TRUE;
232 /*gint ButtonClear(GtkWidget *Widget, GdkEventKey *Event, gpointer *Data)
234 CGtkDisplayer *disp = (CGtkDisplayer *) Data;
236 disp->clear ();
237 return TRUE;
242 // the user typed command, execute it
243 gint cbValidateCommand (GtkWidget *widget, GdkEvent *event, gpointer data)
245 string cmd = gtk_entry_get_text (GTK_ENTRY(widget));
246 CommandHistory.push_back (cmd);
247 // execute the command
248 if(Log == NULL)
249 Log = InfoLog;
250 ICommand::execute (cmd, *Log);
251 // clear the input text
252 gtk_entry_set_text (GTK_ENTRY(widget), "");
253 CommandHistoryPos = CommandHistory.size();
254 return TRUE;
258 void CGtkDisplayer::setTitleBar (const string &titleBar)
260 string wn;
261 if (!titleBar.empty())
263 wn += titleBar;
264 wn += ": ";
266 wn += "Nel Service Console (compiled " __DATE__ " " __TIME__ " in " + nlMode + " mode)";
268 //nlassert (RootWindow != NULL);
269 gtk_window_set_title (GTK_WINDOW (RootWindow), wn.c_str());
272 void CGtkDisplayer::open (std::string titleBar, bool iconified, sint x, sint y, sint w, sint h, sint hs, sint fs, const std::string &fn, bool ww, CLog *log)
274 _HistorySize = hs;
276 if (w == -1)
277 w = 700;
278 if (h == -1)
279 h = 300;
280 if (hs == -1)
281 hs = 10000;
283 gtk_init (NULL, NULL);
285 Log = log;
287 // Root window
288 RootWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
289 gtk_window_set_default_size (GTK_WINDOW (RootWindow), w, h);
290 gtk_signal_connect (GTK_OBJECT (RootWindow), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL);
292 // Vertical box
293 GtkWidget *vrootbox = gtk_vbox_new (FALSE, 0);
294 nlassert (vrootbox != NULL);
295 gtk_container_add (GTK_CONTAINER (RootWindow), vrootbox);
297 // Horizontal box (for labels)
298 hrootbox = gtk_hbox_new (FALSE, 0);
299 nlassert (hrootbox != NULL);
300 gtk_box_pack_start (GTK_BOX (vrootbox), hrootbox, FALSE, FALSE, 0);
302 /* // Clear button
303 GtkWidget *button = gtk_button_new_with_label ("Clear");
304 nlassert (button != NULL);
305 gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (ButtonClear), (gpointer) this);
306 gtk_box_pack_start (GTK_BOX (hrootbox), button, FALSE, FALSE, 0);
308 // Output text
309 scrolled_win2 = gtk_scrolled_window_new (NULL, NULL);
310 nlassert (scrolled_win2 != NULL);
311 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win2), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
312 gtk_widget_show (scrolled_win2);
313 gtk_container_add (GTK_CONTAINER (vrootbox), scrolled_win2);
315 OutputText = gtk_text_view_new();
316 nlassert (OutputText != NULL);
318 PangoFontDescription *fontDesc = pango_font_description_from_string("Monospace 10");
319 gtk_widget_modify_font(OutputText, fontDesc);
320 pango_font_description_free(fontDesc);
322 GtkTextBuffer *textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(OutputText));
323 GtkTextIter endIter;
324 gtk_text_buffer_get_end_iter(textBuffer, &endIter);
325 gtk_text_buffer_create_mark(textBuffer, "endmark", &endIter, false);
326 gtk_signal_connect(GTK_OBJECT(OutputText),"key_press_event",GTK_SIGNAL_FUNC(KeyOut),NULL);
327 gtk_text_view_set_editable (GTK_TEXT_VIEW(OutputText), FALSE);
328 gtk_container_add (GTK_CONTAINER (scrolled_win2), OutputText);
330 // Input text
331 InputText = gtk_entry_new ();
332 nlassert (InputText != NULL);
333 gtk_signal_connect (GTK_OBJECT(InputText), "activate", GTK_SIGNAL_FUNC(cbValidateCommand), NULL);
334 gtk_signal_connect(GTK_OBJECT(InputText),"key_press_event",GTK_SIGNAL_FUNC(KeyIn),NULL);
335 gtk_box_pack_start (GTK_BOX (vrootbox), InputText, FALSE, FALSE, 0);
337 // gtk_widget_show (button);
338 gtk_widget_show (OutputText);
339 gtk_widget_show (InputText);
341 gtk_widget_show (hrootbox);
342 gtk_widget_show (vrootbox);
343 gtk_widget_show (RootWindow);
345 setTitleBar (titleBar);
347 _Init = true;
350 void CGtkDisplayer::clear ()
352 GtkTextBuffer *buffer;
353 GtkTextIter start, end;
355 // who is taking care of the iterators?
356 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(OutputText));
357 gtk_text_buffer_get_bounds(buffer, &start, &end);
358 gtk_text_buffer_delete(buffer, &start, &end);
361 gint updateInterf (gpointer data)
363 CGtkDisplayer *disp = (CGtkDisplayer *)data;
366 // Update labels
369 disp->updateLabels ();
372 // Display the bufferized string
374 GtkAdjustment *Adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolled_win2));
375 bool Bottom = (Adj->value >= Adj->upper - Adj->page_size - Adj->step_increment);
376 bool textChanged = false;
378 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(OutputText));
379 GtkTextIter end_iter;
380 gtk_text_buffer_get_end_iter(buffer, &end_iter);
382 std::list<std::pair<uint32, std::string> >::iterator it;
384 CSynchronized<std::list<std::pair<uint32, std::string> > >::CAccessor access (&disp->_Buffer);
386 for (it = access.value().begin(); it != access.value().end(); it++)
388 uint32 col = (*it).first;
389 GtkTextTag *tag = NULL;
390 if ((col>>24) == 0)
392 GdkColor color;
393 color.red = (col >> 8) & 0xFF00;
394 color.green = col & 0xFF00;
395 color.blue = (col << 8) & 0xFF00;
396 tag = gtk_text_buffer_create_tag(buffer, NULL, "foreground-gdk", &color, "foreground-set", TRUE, NULL);
398 gtk_text_buffer_insert_with_tags(buffer, &end_iter, (*it).second.c_str(), -1, tag, NULL);
399 textChanged = true;
402 access.value().clear ();
405 if (Bottom && textChanged)
407 GtkTextMark *mark = gtk_text_buffer_get_mark(buffer, "endmark");
408 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(OutputText), mark, 0.0, true, 0.0, 1.0);
411 return TRUE;
414 void CGtkDisplayer::display_main ()
417 // Manage windows message
420 gtk_timeout_add (10, updateInterf, this);
421 gtk_main ();
425 void CGtkDisplayer::getWindowPos (uint32 &x, uint32 &y, uint32 &w, uint32 &h)
427 // todo
428 x = y = w = h = 0;
433 } // NLMISC
435 #else // NL_USE_GTK
437 // remove stupid VC6 warnings
438 void foo_gtk_displayer_cpp() {}
440 #endif // NL_USE_GTK