/** * \file xforms/lyx_gui.C * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Lars Gullik Bjønnes * \author John Levon * * Full author contact details are available in file CREDITS. */ #include #include "lyx_gui.h" #include "ColorHandler.h" #include "xfont_loader.h" #include "xforms_helpers.h" #include "xformsImage.h" #include "XFormsView.h" #include "bufferlist.h" #include "BufferView.h" #include "debug.h" #include "funcrequest.h" #include "gettext.h" #include "LColor.h" #include "lyx_main.h" #include "LyXAction.h" #include "lyxfunc.h" #include "lyxrc.h" #include "session.h" #include "lyxserver.h" #include "lyxsocket.h" #include "graphics/LoaderQueue.h" #include "support/filetools.h" #include "support/lyxlib.h" #include "support/os.h" #include "support/package.h" #include "support/convert.h" #include "lyx_forms.h" #include #include #include #include using lyx::support::addName; using lyx::support::package; using lyx::frontend::fontloader; using lyx::frontend::lyxColorHandler; using lyx::frontend::LyXColorHandler; using lyx::frontend::XformsColor; using lyx::frontend::XFormsView; namespace os = lyx::support::os; #ifndef CXX_GLOBAL_CSTD using std::exit; #endif using std::dec; using std::endl; using std::hex; using std::setbase; using std::setfill; using std::setw; using std::ostringstream; using std::vector; using std::string; extern BufferList bufferlist; // FIXME: wrong place ! LyXServer * lyxserver; LyXServerSocket * lyxsocket; namespace { /// quit lyx bool finished = false; /// estimate DPI from X server int getDPI() { Screen * scr = ScreenOfDisplay(fl_get_display(), fl_screen); return int(((HeightOfScreen(scr) * 25.4 / HeightMMOfScreen(scr)) + (WidthOfScreen(scr) * 25.4 / WidthMMOfScreen(scr))) / 2); } /// set default GUI configuration void setDefaults() { FL_IOPT cntl; cntl.buttonFontSize = FL_NORMAL_SIZE; cntl.browserFontSize = FL_NORMAL_SIZE; cntl.labelFontSize = FL_NORMAL_SIZE; cntl.choiceFontSize = FL_NORMAL_SIZE; cntl.inputFontSize = FL_NORMAL_SIZE; cntl.menuFontSize = FL_NORMAL_SIZE; cntl.borderWidth = -1; cntl.vclass = FL_DefaultVisual; fl_set_defaults(FL_PDVisual | FL_PDButtonFontSize | FL_PDBrowserFontSize | FL_PDLabelFontSize | FL_PDChoiceFontSize | FL_PDInputFontSize | FL_PDMenuFontSize | FL_PDBorderWidth, &cntl); } extern "C" { int LyX_XErrHandler(Display * display, XErrorEvent * xeev) { // We don't abort on BadWindow if (xeev->error_code == BadWindow) { lyxerr << "BadWindow received !" << endl; lyxerr << "If you're using xforms 1.0 or greater, " << " please report this to lyx-devel@lists.lyx.org" << endl; return 0; } // emergency cleanup LyX::cref().emergencyCleanup(); // Get the reason for the crash. char etxt[513]; XGetErrorText(display, xeev->error_code, etxt, 512); lyxerr << etxt << " id: " << xeev->resourceid << endl; // By doing an abort we get a nice backtrace. (hopefully) lyx::support::abort(); return 0; } } /// read in geometry specification char geometry[40]; } // namespace anon namespace lyx_gui { bool use_gui = true; void parse_init(int & argc, char * argv[]) { setDefaults(); FL_CMD_OPT cmdopt[] = { {"-geometry", "*.geometry", XrmoptionSepArg, "690x510"} }; FL_resource res[] = { {"geometry", "geometryClass", FL_STRING, geometry, "", 40} }; const int num_res = sizeof(res)/sizeof(FL_resource); fl_initialize(&argc, argv, "LyX", cmdopt, num_res); // It appears that, in xforms >=0.89.5, fl_initialize() // calls setlocale() and ruins our LC_NUMERIC setting. locale_init(); fl_get_app_resources(res, num_res); Display * display = fl_get_display(); if (!display) { lyxerr << "LyX: unable to access X display, exiting" << endl; ::exit(1); } fcntl(ConnectionNumber(display), F_SETFD, FD_CLOEXEC); XSetErrorHandler(LyX_XErrHandler); lyxColorHandler.reset(new LyXColorHandler); using namespace lyx::graphics; // connect the image loader based on the xforms library Image::newImage = boost::bind(&xformsImage::newImage); Image::loadableFormats = boost::bind(&xformsImage::loadableFormats); // must do this /before/ lyxrc gets read lyxrc.dpi = getDPI(); LoaderQueue::setPriority(10,100); } void parse_lyxrc() { XformsColor::read(addName(package().user_support(), "preferences.xform")); if (lyxrc.popup_font_encoding.empty()) lyxrc.popup_font_encoding = lyxrc.font_norm; // Set the font name for popups and menus string boldfontname = lyxrc.popup_bold_font + "-*-*-*-?-*-*-*-*-" + lyxrc.popup_font_encoding; // "?" means "scale that font" string fontname = lyxrc.popup_normal_font + "-*-*-*-?-*-*-*-*-" + lyxrc.popup_font_encoding; int bold = fl_set_font_name(FL_BOLD_STYLE, boldfontname.c_str()); int normal = fl_set_font_name(FL_NORMAL_STYLE, fontname.c_str()); if (bold < 0) lyxerr << "Could not set menu font to " << boldfontname << endl; if (normal < 0) lyxerr << "Could not set popup font to " << fontname << endl; if (bold < 0 && normal < 0) { lyxerr << "Using 'helvetica' font for menus" << endl; boldfontname = "-*-helvetica-bold-r-*-*-*-?-*-*-*-*-iso8859-1"; fontname = "-*-helvetica-medium-r-*-*-*-?-*-*-*-*-iso8859-1"; bold = fl_set_font_name(FL_BOLD_STYLE, boldfontname.c_str()); normal = fl_set_font_name(FL_NORMAL_STYLE, fontname.c_str()); if (bold < 0 && normal < 0) { lyxerr << "Could not find helvetica font. Using 'fixed'." << endl; fl_set_font_name(FL_NORMAL_STYLE, "fixed"); normal = bold = 0; } } if (bold < 0) fl_set_font_name(FL_BOLD_STYLE, fontname.c_str()); else if (normal < 0) fl_set_font_name(FL_NORMAL_STYLE, boldfontname.c_str()); fl_setpup_fontstyle(FL_NORMAL_STYLE); fl_setpup_fontsize(FL_NORMAL_SIZE); fl_setpup_color(FL_MCOL, FL_BLACK); fl_set_goodies_font(FL_NORMAL_STYLE, FL_NORMAL_SIZE); fl_set_tooltip_font(FL_NORMAL_STYLE, FL_NORMAL_SIZE); } void start(string const & batch, vector const & files) { // initial geometry int xpos = -1; int ypos = -1; unsigned int width = 690; unsigned int height = 510; // first try lyxrc if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) { width = lyxrc.geometry_width; height = lyxrc.geometry_height; } // if lyxrc returns (0,0), then use session info else { string val = LyX::ref().session().loadSessionInfo("WindowWidth"); if (val != "") width = convert(val); val = LyX::ref().session().loadSessionInfo("WindowHeight"); if (val != "") height = convert(val); } int const geometryBitmask = XParseGeometry(geometry, &xpos, &ypos, &width, &height); // if width is not set by geometry, check it against monitor width if (!(geometryBitmask & WidthValue)) { Screen * scr = ScreenOfDisplay(fl_get_display(), fl_screen); if (WidthOfScreen(scr) - 8 < int(width)) width = WidthOfScreen(scr) - 8; } // if height is not set by geometry, check it against monitor height if (!(geometryBitmask & HeightValue)) { Screen * scr = ScreenOfDisplay(fl_get_display(), fl_screen); if (HeightOfScreen(scr) - 24 < int(height)) height = HeightOfScreen(scr) - 24; } Screen * s = ScreenOfDisplay(fl_get_display(), fl_screen); // recalculate xpos if it's not set if (xpos == -1) xpos = (WidthOfScreen(s) - width) / 2; // recalculate ypos if it's not set if (ypos == -1) ypos = (HeightOfScreen(s) - height) / 2; lyxerr[Debug::GUI] << "Creating view: " << width << 'x' << height << '+' << xpos << '+' << ypos << endl; boost::shared_ptr view(new XFormsView(width, height)); LyX::ref().addLyXView(view); view->show(xpos, ypos, "LyX"); view->init(); // FIXME: some code below needs moving lyxserver = new LyXServer(&view->getLyXFunc(), lyxrc.lyxpipes); lyxsocket = new LyXServerSocket(&view->getLyXFunc(), os::internal_path(package().temp_dir() + "/lyxsocket")); for_each(files.begin(), files.end(), bind(&BufferView::loadLyXFile, view->view(), _1, true)); // handle the batch commands the user asked for if (!batch.empty()) view->getLyXFunc().dispatch(lyxaction.lookupFunc(batch)); // enter the event loop while (!finished) { if (fl_check_forms() == FL_EVENT) { XEvent ev; fl_XNextEvent(&ev); lyxerr[Debug::GUI] << "Received unhandled X11 event" << endl << "Type: " << ev.xany.type << " Target: 0x" << hex << ev.xany.window << dec << endl; } } // FIXME: breaks emergencyCleanup delete lyxsocket; delete lyxserver; } void exit() { finished = true; } void sync_events() { // FIXME } FuncStatus getStatus(FuncRequest const & /*ev*/) { // Nothing interesting to do here return FuncStatus(); } bool getRGBColor(LColor_color col, lyx::RGBColor & rgbcol) { string const name = lcolor.getX11Name(col); Display * const display = fl_get_display(); Colormap const cmap = fl_state[fl_get_vclass()].colormap; XColor xcol, ccol; if (XLookupColor(display, cmap, name.c_str(), &xcol, &ccol) == 0) { rgbcol.r = 0; rgbcol.g = 0; rgbcol.b = 0; return false; } // Note that X stores the RGB values in the range 0 - 65535 // whilst we require them in the range 0 - 255. rgbcol.r = xcol.red / 256; rgbcol.g = xcol.green / 256; rgbcol.b = xcol.blue / 256; return true; } string const hexname(LColor_color col) { lyx::RGBColor rgbcol; if (!getRGBColor(col, rgbcol)) { lyxerr << "X can't find color for \"" << lcolor.getLyXName(col) << '"' << endl; return string(); } ostringstream os; os << setbase(16) << setfill('0') << setw(2) << rgbcol.r << setw(2) << rgbcol.g << setw(2) << rgbcol.b; return os.str(); } void update_color(LColor_color col) { lyxColorHandler->getGCForeground(col); lyxColorHandler->updateColor(col); } void update_fonts() { fontloader.update(); } bool font_available(LyXFont const & font) { return fontloader.available(font); } namespace { std::map > socket_callbacks; extern "C" void C_socket_callback(int fd, void *) { socket_callbacks[fd](); } } // NS anon void register_socket_callback(int fd, boost::function func) { socket_callbacks[fd] = func; fl_add_io_callback(fd, FL_READ, C_socket_callback, 0); } void unregister_socket_callback(int fd) { fl_remove_io_callback(fd, FL_READ, C_socket_callback); socket_callbacks.erase(fd); } string const roman_font_name() { return "times"; } string const sans_font_name() { return "helvetica"; } string const typewriter_font_name() { return "courier"; } }; // namespace lyx_gui