| /* |
| * Easygl Version 3.0 |
| * Originally written by Vaughn Betz (vaughn@eecg.utoronto.ca) |
| * Win32 port by Paul Leventis (pleventi@altera.com) |
| * Enhanced version by William Chow (wchow@altera.com) |
| * Minor updates by Guy Lemieux (lemieux@ece.ubc.ca) |
| * More updates by Vaughn Betz to make win32 cleaner and more robust. |
| * More updates and code cleanup by Long Yu Wang (longyu.wang@mail.utoronto.ca) |
| * More updates and c++ integration - Matthew J.P. Walker (matthewjp.walker@mail.utoronto.ca) |
| * Cairo integration and other updates by Harry Liang and Davis Wu |
| * |
| * All rights reserved by U of T, etc. * |
| * * |
| * You may freely use this graphics interface for non-commercial purposes * |
| * as long as you leave the author info above in it. * |
| * * |
| * Revision History: * |
| * * |
| * V4.0 Jan. 2017 - Harry Liang and Davis Wu * |
| * - Integrated with Cairo to allow transparency. Direct X11 drawing used * |
| * when alpha = 1 (255) as it is faster than using Cairo's layer above * |
| * X11. Also added double buffering, png file bitmap drawing, and the * |
| * ability to draw in screen coordinates that don't pan or zoom. * |
| * * |
| * V3.0 May 2014 - June 2014 (Matthew J.P. Walker) * |
| * - continued integration with c++ * |
| * - added ybounding and convenience functions to text drawing * |
| * - added get_visible_world() - returns a rectangle describing the bounds * |
| * of the visible world. * |
| * - Panning and zooming will drop events instead of drawing all of them. * |
| * Much more usable on large drawings now. * |
| * - Added support for aribtrary RGB color triplets to X11, windows and ps. * |
| * - Modernized X11 font handling and text drawing by converting it to Xft. * |
| * - Added text rotation to X11, windows and postscript. * |
| * - Changed font caching, removing font size limit. * |
| * - Added unicode support in all text, via UTF-8. * |
| * * |
| * V2.0.2 May 2013 - June 2013 (Mike Wang) * |
| * - In Win32, removed "Window" operation with right mouse click to align * |
| * with X11. * |
| * - Fixed a bug in Win32 where when the "Window" button is clicked, if the * |
| * application window gets minimized then returned back up, the screen * |
| * background would not be redrawn before new rubber band gets drawn. * |
| * - Grouped file scope variables into structures, factored out functions * |
| * that were too long, and formatted code spacings etc. * |
| * - Cleaned up Win32 code by removing the inefficient saving and updating * |
| * of graphics contexts. * |
| * - Added zooming with respect to cursor location. * |
| * - Added panning with middle mouse button (or mousewheel) click and drag. * |
| * - Ran experiment to test the impact of rect_off_screen() on drawscreen() * |
| * runtime. Found rect_off_screen() to be effective in speeding up screen * |
| * redraw. * |
| * - Added zooming using mousewheel. * |
| * * |
| * V2.0.1 Sept. 2012 (Vaughn Betz) * |
| * - Fixed a bug in Win32 where postscript output would make the graphics * |
| * crash when you redrew. * |
| * - Made a cleaner makefile to simplify platform selection. * |
| * - Commented and reorganized some of the code. Started cleaning up some * |
| * of the win32 code. Win32 looks inefficient; it is saving and updating * |
| * graphics contexts all the time even though we know when the context is * |
| * valid vs. not-valid. * |
| * TODO: make win32 work more like X11 (minimize gc updates). * |
| * * |
| * V2.0: Nov. 21, 2011 (Vaughn Betz) * |
| * - Updated example code, and some cleanup and bug fixes to win32 code. * |
| * - Removed some win32 code that had no X11 equivalent or wasn't well * |
| * documented. * |
| * - Used const char * where appropriate to get rid of g++ warnings. * |
| * - Made interface to things like xor drawing more consistent, and added * |
| * to example program. * |
| * - Made a simpler (easygl.cpp) interface to the graphics library for * |
| * use by undergraduate students. * |
| * * |
| * V1.06 : July 23, 2003 : (Guy Lemieux) * |
| * - added some typecasts to cleanly compile with g++ and MS c++ tools * |
| * - if WIN32 not defined, it defines X11 automatically * |
| * - fixed X11 compilation; WIN32 broke some things * |
| * * |
| * V1.05 : July 26, 2001 : (William) * |
| * - changed keyboard detect function to accept an int (virtual key) * |
| * * |
| * V1.04 : June 29, 2001 : (William) * |
| * - added drawcurve(), fillcurve() using Bezier curves * |
| * (support WIN32 screen / ps) * |
| * - added pt on object capability : using a memory buffer to draw an * |
| * graphics objects, then query if a point fall on the object (bear the * |
| * object's colour) : object_start(), object_end(), pt_on_object() * |
| * - added drawellipticarc(), fillellipticarc() * |
| * - added findfontsize() to help find a pointsize of a given height * |
| * - extended t_report to keep xleft, xright, ytop, ybot * |
| * - added update_window() to set the window bb * |
| * * |
| * V1.03 : June 18, 2001 : (William) * |
| * - added change_button_text() * |
| * * |
| * V1.02 : June 13, 2001 : (William) * |
| * - extension to mouse click function : can tell if ctrl/shift keys are * |
| * pressed * |
| * * |
| * V1.01 : June 1, 2001 : (William) * |
| * - add tooltip support * |
| * * |
| * V1.0 : May 14, 2001 : (William) * |
| * - fixed a problem with line styles, initial release on the internet * |
| * * |
| * March 27, 2001 : (William) * |
| * - added setcolor_by_colorref to make more colors available (in Win32) * |
| * * |
| * February 16, 2001 : (William) * |
| * - added quick zoom using right mouse clicks * |
| * * |
| * February 11, 2001 : (William) * |
| * - can define cleanup(), passed in when calling init_graphics(), and * |
| * called when shutting down * |
| * * |
| * February 1, 2001 : (William) * |
| * - fix xor mode redraw problem * |
| * * |
| * September 19, 2000 : (William) * |
| * - can define mouse_move callback function * |
| * - can add separators in between buttons * |
| * * |
| * September 8, 2000 : (William) * |
| * - added result_structure(), * |
| * - can define background color in init_graphics * |
| * * |
| * August 10, 2000 : (William Chow, choww@eecg.utoronto.ca) * |
| * - Finished all Win32 support functions * |
| * - use XOR mode for window zooming box * |
| * - added double buffering feature * |
| * * |
| * January 12, 1999: (Paul) * |
| * - Fixed a bunch of stuff with the Win32 support (memory leaks, etc) * |
| * - Made the clipping function using the update rectangle for Win32 * |
| * * |
| * January 9, 1999: (Paul Leventis, leventi@eecg.utoronto.ca) * |
| * - Added Win32 support. Should work under Windows98/95/NT 4.0/NT 5.0. * |
| * - Added a check to deselect_all to determine whether the screen needs to * |
| * be updated or not. Should elminate flicker from mouse clicks * |
| * - Added win32_invalidate_screen() call to graphics.c that in turn calls * |
| * update_screen, so this function was made non-static and added to the * |
| * header file. This is due to differences in the structure of Win32 * |
| * windowing apps. * |
| * - Win32 needs clipping (though done automatically, could be faster) * |
| * * |
| * Sept. 19, 1997: Incorporated Zoom Fit code of Haneef Mohammed at * |
| * Cypress. Makes it easy to zoom to a full view of the graphics. * |
| * * |
| * Sept. 11, 1997: Added the create_and destroy_button interface to * |
| * make it easy to add and destroy buttons from user code. Removed the * |
| * bnum parameter to the button functions, since it wasn't really needed. * |
| * * |
| * June 28, 1997: Added filled arc drawing primitive. Minor modifications * |
| * to PostScript driver to make the PostScript output slightly smaller. * |
| * * |
| * April 15, 1997: Added code to init_graphics so it waits for a window * |
| * to be exposed before returning. This ensures that users of non- * |
| * interactive graphics can never draw to a window before it is available. * |
| * * |
| * Feb. 24, 1997: Added code so the package will allocate a private * |
| * colormap if the default colormap doesn't have enough free colours. * |
| * * |
| * June 28, 1996: Converted all internal functions in graphics.c to have * |
| * internal (static) linkage to avoid any conflicts with user routines in * |
| * the rest of the program. * |
| * * |
| * June 12, 1996: Added setfontsize and setlinewidth attributes. Added * |
| * pre-clipping of objects for speed (and compactness of PS output) when * |
| * graphics are zoomed in. Rewrote PostScript engine to shrink the output * |
| * and make it easier to read. Made drawscreen a callback function passed * |
| * in rather than a global. Graphics attribute calls are more efficient -- * |
| * they check if they have to change anything before doing it. * |
| * * |
| * October 27, 1995: Added the message area, a callback function for * |
| * interacting with user button clicks, and implemented a workaround for a * |
| * Sun X Server bug that misdisplays extremely highly zoomed graphics. * |
| * * |
| * Jan. 13, 1995: Modified to incorporate PostScript Support. */ |
| |
| /**************************** Top-level summary ****************************** |
| This graphics package provides API for client program that wishes to implement |
| a graphical user interface. The client program will first call init_graphics() |
| for initial setup, which includes opening up an application window, setting up |
| the specified window_name and background colour, creating sublevel windows, |
| etc. Then the program will call a few more setup functions such as |
| set_visible_world(), which sets up world coordinates, create_button(), which sets up |
| menu buttons, and update_message(), which updates status bar message. After |
| all the setup, the program will call the main routine for the graphics, |
| event_loop(), which will take control of the program until the "Proceed" |
| button is pressed. Event_loop() directly communicates with X11/Win32 to |
| receive event notifications, and it either calls other event-handling |
| functions in the graphics package or callbacks passed as function pointers |
| from the client program. The most important callback function which the |
| client should provide is drawscreen(). Whenever the graphics need to be |
| redrawn, drawscreen() will be called. There are a number drawing routines |
| which the client can use to update the graphic contexts (ie. setcolor, |
| setfontsize, setlinewidth, and setlinestyle) and draw simple shapes as |
| desired. When the program is done executing the graphics, it should call |
| close_graphics() to release all drawing structures and close the graphics.*/ |
| |
| #ifndef NO_GRAPHICS // Strip everything out and just put in stubs if NO_GRAPHICS defined |
| |
| /********************************** |
| * Common Preprocessor Directives * |
| **********************************/ |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <cmath> |
| #include <string> |
| #include <iostream> |
| #include <algorithm> |
| #include <vector> |
| #include <queue> |
| #include <unordered_map> |
| #include <sys/timeb.h> |
| #include "graphics.h" |
| #include "graphics_state.h" |
| #include "graphics_automark.h" |
| |
| #include "SurfaceImpl.h" //Needed by draw_surface to access the underlying surface type |
| |
| using namespace std; |
| |
| |
| #if defined(X11) || defined(WIN32) |
| |
| // VERBOSE is very helpful for developing event handling features. Outputs |
| // useful information when user interacts with the graphic interface. |
| // Uncomment the line below to turn on VERBOSE. |
| //#define VERBOSE |
| |
| #define MWIDTH 104 /* Width of menu window */ |
| #define MENU_FONT_SIZE 11 /* Font for menus and dialog boxes. */ |
| #define T_AREA_HEIGHT 24 /* Height of text window */ |
| |
| #define BUTTON_TEXT_LEN 100 |
| |
| #define ZOOM_FACTOR 1.6667 /* For zooming on the graphics */ |
| #endif |
| |
| #ifdef X11 |
| |
| /********************************************* |
| * X-Windows Specific Preprocessor Directives * |
| *********************************************/ |
| |
| #include <X11/Xlib.h> |
| #include <X11/Xutil.h> |
| #include <X11/Xos.h> |
| #include <X11/Xatom.h> |
| #include <X11/Xft/Xft.h> |
| |
| // Some X11 implementations overflow with sufficiently large pixel |
| // coordinates and start drawing strangely. We will clip all pixels |
| // to lie in the range below. Don't want to make this too small as |
| // clipping an x-coordinate but not a y-coordinate can happen, |
| // and that makes the slope of lines change, etc. |
| #define MAXPIXEL 32000.f |
| #define MINPIXEL -32000.f |
| |
| /* Uncomment the line below if your X11 header files don't define XPointer */ |
| /* typedef char *XPointer; */ |
| |
| #endif /* X11 Preprocessor Directives */ |
| |
| #ifdef WIN32 |
| |
| /************************************************************* |
| * Microsoft Windows (WIN32) Specific Preprocessor Directives * |
| *************************************************************/ |
| |
| #pragma warning(disable : 4996) // Turn off annoying warnings about strcmp. |
| |
| #ifndef UNICODE |
| #define UNICODE // force windows api into unicode (usually UTF-16) mode. |
| #endif |
| |
| #include <windows.h> |
| #include <WindowsX.h> |
| |
| /* Using large values of pixel limits in order to preserve the slope of diagonal |
| * lines when their endpoints are clipped (one at a time) to these pixel limits. |
| * However, if these values are too large, then Windows will not draw the lines |
| * when zoomed way in. I have increased MAXPIXEL and MINPIXEL to these values so |
| * the diagonal lines do not change slope when zoomed way in. Also, I have tested |
| * to make sure these values are safe. Adding one more digit to these values |
| * may cause problems when zoomed way in. |
| * MW, June 2013. |
| */ |
| #define MAXPIXEL 21474836.f |
| #define MINPIXEL -21474836.f |
| |
| /* Windows work in degrees, where as X11 and PostScript both work in radians. |
| * Thus, a conversion is needed for Windows. |
| */ |
| #define DEGTORAD(x) ((x)/180.*PI) |
| #endif /* Win32 preprocessor Directives */ |
| |
| |
| /************************************************************* |
| * Common Structure Definitions * |
| *************************************************************/ |
| |
| /* Indicates if this button displays text, a polygon or is just a separator. |
| */ |
| typedef enum { |
| BUTTON_TEXT = 0, |
| BUTTON_POLY, |
| BUTTON_SEPARATOR |
| } t_button_type; |
| |
| /* Structure used to define the buttons on the right hand side of the main window (menu region). |
| * width, height: button size, in pixels. |
| * xleft, ytop: coordinates, in pixels, of the top-left corner of the button relative to its |
| * containing (menu) window. |
| * fcn: a callback function that is called when the button is pressed. This function takes one |
| * argument, a function pointer to the routine that can draw the graphics area (user routine). |
| * win, hwnd: X11 and Win32 data pointer to the window, respectively. |
| * button_type: indicates if this button displays text, a polygon or is just a separator. |
| * text: the text to display if this is a text button. |
| * poly: the polygon (up to 3 points right now) to display if this is a polygon button |
| * is_pressed: has the button been pressed, and is currently executing its callback? |
| * is_enabled: can you press this button right now? Visually will look "pushed in" when |
| * not enabled, and won't respond to clicks. |
| */ |
| typedef struct { |
| int width; |
| int height; |
| int xleft; |
| int ytop; |
| void(*fcn) (void(*drawscreen) (void)); |
| #ifdef X11 |
| Window win; |
| XftDraw* draw; |
| #else |
| HWND hwnd; |
| #endif |
| t_button_type type; |
| char text[BUTTON_TEXT_LEN]; //Space for terminator |
| int poly[3][2]; |
| bool ispressed; |
| bool enabled; |
| } t_button; |
| |
| /* Structure used to store all the buttons created in the menu region. |
| * button: array of pointers to all the buttons created [0..num_buttons-1] |
| * num_buttons: number of menu buttons created |
| */ |
| typedef struct { |
| t_button *button; |
| int num_buttons; |
| } t_button_state; |
| |
| /* Structure used to store coordinate information used for |
| * graphic transformations. |
| * display_width and display_height: entire screen size |
| * top_width and top_height: application window size in pixels |
| * init_xleft, init_xright, init_ytop, and init_ybot: initial world coordinates |
| * (for use in zoom_fit() to restore initial coordinates) |
| * xleft, xright, ytop, and ybot: boundaries of the graphic child window in world |
| * coordinates |
| * wtos_xmult and wtos_ymult: world to screen transformation factors (number of |
| * screen pixels per world coordinate) |
| * stow_xmult and stow_ymult: screen to world transformation factors (number of |
| * world coordinates per screen pixel) |
| * ps_left, ps_right, ps_top, and ps_bot: figure boundaries for PostScript output, |
| * in PostScript (point, 1/72 of inch) coordinates |
| * ps_xmult and ps_ymult: world to PostScript transformation factors (number of |
| * PostScript coordinates per world coordinate) |
| * s_to_ps_xmult and s_to_ps_ymult: Transform from screen (pixel) space to |
| * postscript. typeset points per pixel. |
| */ |
| typedef struct { |
| int display_width, display_height; |
| int top_width, top_height; |
| float init_xleft, init_xright, init_ytop, init_ybot; |
| float xleft, xright, ytop, ybot; |
| float wtos_xmult, wtos_ymult; |
| float stow_xmult, stow_ymult; |
| float ps_left, ps_right, ps_top, ps_bot; |
| float ps_xmult, ps_ymult; |
| float s_to_ps_xmult, s_to_ps_ymult; |
| } t_transform_coordinates; |
| |
| /* Structure used to store state variables used for panning. |
| * previous_x and previous_y: (in window (pixel) coordinates,) last location of |
| * the cursor before new motion |
| * panning_enabled: whether panning is activated or de-activated |
| */ |
| typedef struct { |
| int previous_x, previous_y; |
| bool panning_enabled; |
| } t_panning_state; |
| |
| /********************************************************************* |
| * File scope variables. * |
| *********************************************************************/ |
| |
| static t_gl_state gl_state; |
| |
| // Stores all the menu buttons created. Initialize the button pointer |
| // and num_buttons for safety. |
| static t_button_state button_state = {NULL, 0}; |
| |
| // Contains all coordinates useful for graphics transformation |
| static t_transform_coordinates trans_coord; |
| |
| // Initialize panning_enabled to false, so panning is not activated |
| static t_panning_state pan_state = {0, 0, false}; |
| |
| // assert(t_color::predef_colors.size() == NUM_COLOR); |
| |
| // Color names, also used in postscript generation |
| static const char *ps_cnames[NUM_COLOR] = { |
| "white", |
| "black", |
| "grey55", |
| "grey75", |
| "red", |
| "orange", |
| "yellow", |
| "green", |
| "cyan", |
| "blue", |
| "purple", |
| "pink", |
| "lightpink", |
| "darkgreen", |
| "magenta", |
| "bisque", |
| "lightskyblue", |
| "thistle", |
| "plum", |
| "khaki", |
| "coral", |
| "turquoise", |
| "mediumpurple", |
| "darkslateblue", |
| "darkkhaki", |
| "lightmediumblue", // (Non X11, but that won't be a problem) |
| "saddlebrown", |
| "firebrick", |
| "limegreen" |
| }; |
| |
| #ifdef X11 |
| |
| /************************************************* |
| * X-Windows Specific File-scope Variables * |
| *************************************************/ |
| |
| // Stores all state variables for X Windows |
| t_x11_state x11_state; |
| |
| #endif /* X11 file scope variables */ |
| |
| #ifdef WIN32 |
| |
| /***************************************************** |
| * Microsoft Windows (Win32) File Scope Variables * |
| *****************************************************/ |
| |
| /* Linestyle references for Win32. */ |
| static const int win32_line_styles[2] = {PS_SOLID, PS_DASH}; |
| |
| /* Name of each window */ |
| static wchar_t szAppName[256], szGraphicsName[] = L"VPR Graphics", |
| szStatusName[] = L"VPR Status", szButtonsName[] = L"VPR Buttons"; |
| |
| /* Stores all state variables for Win32 */ |
| static t_win32_state win32_state = {false, WINDOW_DEACTIVATED, -1}; |
| |
| #endif /* WIN32 file scope variables */ |
| |
| |
| /********************************************* |
| * Common Subroutine Declarations * |
| *********************************************/ |
| |
| static void *my_malloc(int ibytes); |
| static void *my_realloc(void *memblk, int ibytes); |
| |
| /* translation from screen coordinates to the world coordinates * |
| * used by the client program. */ |
| static float xscrn_to_world(int x); |
| static float yscrn_to_world(int y); |
| |
| /* translation from world to screen coordinates */ |
| static int xworld_to_scrn(float worldx); |
| static int yworld_to_scrn(float worldy); |
| static float xworld_to_scrn_fl(float worldx); // without rounding to nearest pixel |
| static float yworld_to_scrn_fl(float worldy); |
| static float xrad_to_scrn (float xrad); |
| static float yrad_to_scrn (float yrad); |
| |
| /* translation from world to PostScript coordinates */ |
| static float x_to_post(float worldx); |
| static float y_to_post(float worldy); |
| |
| static void force_setcolor(int cindex); |
| static void force_setcolor(const t_color& new_color); |
| static void update_brushes(); |
| static void force_setlinestyle(int linestyle, int capstyle = CapButt); |
| static void force_setlinewidth(int linewidth); |
| static void force_settextattrs(int pointsize, int degrees); |
| |
| static bool use_cairo(); |
| |
| static void reset_common_state(); |
| static void build_default_menu(void); |
| |
| /* Function declarations for button responses */ |
| static void translate_up(void (*drawscreen) (void)); |
| static void translate_left(void (*drawscreen) (void)); |
| static void translate_right(void (*drawscreen) (void)); |
| static void translate_down(void (*drawscreen) (void)); |
| static void panning_execute(int x, int y, void (*drawscreen) (void)); |
| static void panning_on(int start_x, int start_y); |
| static void panning_off(void); |
| static void zoom_in(void (*drawscreen) (void)); |
| static void zoom_out(void (*drawscreen) (void)); |
| static void handle_zoom_in(float x, float y, void (*drawscreen) (void)); |
| static void handle_zoom_out(float x, float y, void (*drawscreen) (void)); |
| static void zoom_fit(void (*drawscreen) (void)); |
| static void adjustwin(void (*drawscreen) (void)); |
| static void postscript(void (*drawscreen) (void)); |
| static void proceed(void (*drawscreen) (void)); |
| static void quit(void (*drawscreen) (void)); |
| static void map_button(int bnum); |
| static void unmap_button(int bnum); |
| |
| static bool is_droppable_event( |
| #ifdef X11 |
| XEvent* event |
| #elif defined WIN32 |
| MSG* message |
| #endif |
| ); |
| |
| #ifdef X11 |
| |
| /**************************************************** |
| * X-Windows Specific Subroutine Declarations * |
| *****************************************************/ |
| |
| /* Helper functions for X11; not visible to client program. */ |
| static void x11_init_graphics(const char* window_name); |
| static void init_cairo(); |
| static void x11_event_loop(void (*act_on_mousebutton) |
| (float x, float y, t_event_buttonPressed button_info), |
| void (*act_on_mousemove)(float x, float y), |
| void (*act_on_keypress)(char key_pressed, int keysym), |
| void (*drawscreen) (void)); |
| |
| static bool x11_drop_redundant_panning (XEvent report, |
| unsigned int& last_skipped_button_press_button); |
| static void x11_redraw_all_if_needed (void (*drawscreen) (void)); |
| static Bool x11_test_if_exposed(Display *disp, XEvent *event_ptr, |
| XPointer dummy); |
| static void x11_build_textarea(void); |
| static void x11_drawbut(int bnum); |
| static int x11_which_button(Window win); |
| |
| static void x11_turn_on_off(int pressed); |
| static void x11_drawmenu(void); |
| static void menutext(XftDraw* draw, int xc, int yc, const char *text); |
| |
| static void x11_handle_expose(XEvent report, void (*drawscreen) (void)); |
| static void x11_handle_configure_notify(XEvent report, void (*drawscreen) (void)); |
| static void x11_handle_button_info(t_event_buttonPressed *button_info, |
| int buttonNumber, int Xbutton_state); |
| |
| static unsigned long x11_convert_to_xcolor (t_color color); |
| |
| #endif /* X11 Declarations */ |
| |
| #ifdef WIN32 |
| |
| /******************************************************************* |
| * Win32-specific subroutine declarations * |
| *******************************************************************/ |
| |
| /* Helper function called by init_graphics(). Not visible to client program. */ |
| static void win32_init_graphics(const char* window_name); |
| |
| /* Callback functions for the top-level window and 3 sub-windows. |
| * Windows uses an odd mix of events and callbacks, so it needs these. |
| */ |
| static LRESULT CALLBACK WIN32_GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); |
| static LRESULT CALLBACK WIN32_StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); |
| static LRESULT CALLBACK WIN32_ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); |
| static LRESULT CALLBACK WIN32_MainWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); |
| |
| // For Win32, need to save pointers to these callback functions at file |
| // scope, since windows has a bizarre event loop structure where you poll |
| // for events, but then dispatch the event and get called via a callback |
| // from windows (WIN32_GraphicsWND, below). I can't figure out why windows |
| // does things this way, but it is what makes saving these function pointers |
| // necessary. VB. |
| static void (*win32_mouseclick_ptr)(float x, float y, t_event_buttonPressed button_info); |
| static void (*win32_mousemove_ptr)(float x, float y); |
| static void (*win32_keypress_ptr)(char entered_char, int keysym); |
| static void (*win32_drawscreen_ptr)(void); |
| |
| // Helper functions for WIN32_GraphicsWND callback function |
| static void win32_GraphicsWND_handle_WM_PAINT(HWND hwnd, PAINTSTRUCT &ps, HPEN &hDotPen, |
| RECT &oldAdjustRect); |
| static void win32_GraphicsWND_handle_WM_LRBUTTONDOWN(UINT message, WPARAM wParam, LPARAM lParam, |
| int &X, int &Y, RECT &oldAdjustRect); |
| static void win32_GraphicsWND_handle_WM_MBUTTONDOWN(HWND hwnd, UINT message, WPARAM wParam, |
| LPARAM lParam); |
| static void win32_GraphicsWND_handle_WM_MOUSEMOVE(LPARAM lParam, int &X, int &Y, |
| RECT &oldAdjustRect); |
| |
| // Functions for displaying errors in a message box on windows. |
| static void WIN32_SELECT_ERROR(); |
| static void WIN32_DELETE_ERROR(); |
| static void WIN32_CREATE_ERROR(); |
| static void WIN32_DRAW_ERROR(); |
| |
| static void win32_invalidate_screen(); |
| |
| static void win32_reset_state(); |
| static void win32_drain_message_queue(); |
| |
| static void win32_handle_mousewheel_zooming(WPARAM wParam, LPARAM lParam, bool draw_screen); |
| static void win32_handle_button_info(t_event_buttonPressed &button_info, UINT message, |
| WPARAM wParam); |
| |
| COLORREF convert_to_win_color(const t_color& src); |
| |
| #endif /* Win32 Declarations */ |
| |
| |
| /********************************************************* |
| * Common Subroutine Definitions * |
| *********************************************************/ |
| |
| /* safer malloc */ |
| static void *my_malloc(int ibytes) { |
| void *mem; |
| |
| mem = static_cast<void*> (malloc(ibytes)); |
| if (mem == NULL) { |
| printf("memory allocation failed!"); |
| exit(-1); |
| } |
| |
| return mem; |
| } |
| |
| /* safer realloc */ |
| static void *my_realloc(void *memblk, int ibytes) { |
| void *mem; |
| |
| mem = static_cast<void*> (realloc(memblk, ibytes)); |
| if (mem == NULL) { |
| printf("memory allocation failed!"); |
| exit(-1); |
| } |
| |
| return mem; |
| } |
| |
| /* translation from screen coordinates to the world coordinates * |
| * in the x direction. */ |
| static float xscrn_to_world(int x) { |
| float world_coord_x; |
| world_coord_x = x * trans_coord.stow_xmult + trans_coord.xleft; |
| |
| return world_coord_x; |
| } |
| |
| /* translation from screen coordinates to the world coordinates * |
| * in the y direction. */ |
| static float yscrn_to_world(int y) { |
| float world_coord_y; |
| world_coord_y = y * trans_coord.stow_ymult + trans_coord.ytop; |
| |
| return world_coord_y; |
| } |
| |
| t_point scrn_to_world(const t_point& point) { |
| return t_point( |
| xscrn_to_world(point.x), |
| yscrn_to_world(point.y) |
| ); |
| } |
| |
| t_bound_box scrn_to_world(const t_bound_box& box) { |
| return t_bound_box( |
| scrn_to_world(box.bottom_left()), |
| scrn_to_world(box.top_right()) |
| ); |
| } |
| |
| /* Translates from world (client program) coordinates to screen coordinates |
| * (pixels) in the x direction. Add 0.5 at end for extra half-pixel accuracy. |
| */ |
| static int xworld_to_scrn(float worldx) { |
| return static_cast<int> (xworld_to_scrn_fl(worldx) + 0.5); |
| } |
| |
| static float xworld_to_scrn_fl(float worldx) { |
| if (gl_state.currentcoordinatesystem == GL_SCREEN) return worldx; |
| |
| float winx; |
| |
| winx = (worldx - trans_coord.xleft) * trans_coord.wtos_xmult; |
| |
| /* Avoids overflow in the X11/Win32 routines. This will allow horizontal |
| * and vertical lines to be drawn correctly regardless of zooming, but * |
| * will cause diagonal lines that go way off screen to change their * |
| * slope as you zoom in. The only way I can think of to completely fix * |
| * this problem is to do all the clipping in advance in floating point, * |
| * then convert to integers and call Windows. This is a lot of extra * |
| * coding, and means that coordinates will be clipped twice, even though * |
| * this "Super Zoom" problem won't occur unless users zoom way in on * |
| * the graphics. */ |
| |
| winx = max(winx, MINPIXEL); |
| winx = min(winx, MAXPIXEL); |
| |
| return (winx); |
| } |
| |
| /* Translates from world (client program) coordinates to screen coordinates |
| * (pixels) in the y direction. Add 0.5 at end for extra half-pixel accuracy. |
| */ |
| static int yworld_to_scrn(float worldy) { |
| return static_cast<int> (yworld_to_scrn_fl(worldy) + 0.5); |
| } |
| |
| static float yworld_to_scrn_fl(float worldy) { |
| if (gl_state.currentcoordinatesystem == GL_SCREEN) return worldy; |
| |
| float winy; |
| |
| winy = (worldy - trans_coord.ytop) * trans_coord.wtos_ymult; |
| |
| /* Avoid overflow in the X/Win32 Window routines. */ |
| winy = max(winy, MINPIXEL); |
| winy = min(winy, MAXPIXEL); |
| |
| return (winy); |
| } |
| |
| t_point world_to_scrn(const t_point& point) { |
| return t_point( |
| xworld_to_scrn_fl(point.x), |
| yworld_to_scrn_fl(point.y) |
| ); |
| } |
| |
| t_bound_box world_to_scrn(const t_bound_box& box) { |
| return t_bound_box( |
| world_to_scrn(box.bottom_left()), |
| world_to_scrn(box.top_right()) |
| ); |
| } |
| |
| |
| // Convert an x-directed radius to screen coordinates (for arc drawing) |
| static float xrad_to_scrn (float xrad) { |
| if (gl_state.currentcoordinatesystem == GL_SCREEN) { |
| return xrad; |
| } |
| else { |
| return (fabs (xrad * trans_coord.wtos_xmult)); |
| } |
| } |
| |
| |
| // Convert an y-directed radius to screen coordinates (for arc drawing) |
| static float yrad_to_scrn (float yrad) { |
| if (gl_state.currentcoordinatesystem == GL_SCREEN) { |
| return yrad; |
| } |
| else { |
| return (fabs (yrad * trans_coord.wtos_ymult)); |
| } |
| } |
| |
| |
| /* translation from world or screen coords to PostScript coordinates in the x direction */ |
| static float x_to_post(float x) { |
| float ps_coord_x; |
| if (gl_state.currentcoordinatesystem == GL_WORLD) |
| ps_coord_x = (x - trans_coord.xleft) * trans_coord.ps_xmult + trans_coord.ps_left; |
| else |
| ps_coord_x = x * trans_coord.s_to_ps_xmult + trans_coord.ps_left; // Screen coordinate left is at 0 |
| |
| return ps_coord_x; |
| } |
| |
| /* translation from world or screen coords to PostScript coordinates in the y direction */ |
| static float y_to_post(float y) { |
| float ps_coord_y; |
| if (gl_state.currentcoordinatesystem == GL_WORLD) |
| ps_coord_y = (y - trans_coord.ybot) * trans_coord.ps_ymult + trans_coord.ps_bot; |
| else |
| ps_coord_y = trans_coord.ps_top + y * trans_coord.s_to_ps_ymult; // Screen coordinate top is at 0 |
| |
| return ps_coord_y; |
| } |
| |
| |
| static bool use_cairo() |
| { |
| return (gl_state.foreground_color.alpha != 255); |
| } |
| |
| /* Sets the current graphics context colour to cindex, regardless of whether we think it is |
| * needed or not. |
| */ |
| static void force_setcolor(int cindex) { |
| gl_state.foreground_color = t_color::predef_colors[cindex]; |
| update_brushes(); |
| } |
| |
| static void force_setcolor(const t_color& new_color) { |
| gl_state.foreground_color = new_color; |
| update_brushes(); |
| } |
| |
| static void update_brushes() { |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef X11 |
| if (gl_state.foreground_color.alpha != 255) |
| { |
| cairo_set_source_rgba(x11_state.ctx, |
| static_cast<double>(gl_state.foreground_color.red)/255, |
| static_cast<double>(gl_state.foreground_color.green)/255, |
| static_cast<double>(gl_state.foreground_color.blue)/255, |
| static_cast<double>(gl_state.foreground_color.alpha)/255); |
| } |
| else |
| { |
| XSetForeground( |
| x11_state.display, |
| x11_state.current_gc, |
| x11_convert_to_xcolor(gl_state.foreground_color) |
| ); |
| } |
| XRenderColor xr_textcolor; |
| xr_textcolor.red = (gl_state.foreground_color.red << 8); |
| xr_textcolor.green = (gl_state.foreground_color.green << 8); |
| xr_textcolor.blue = (gl_state.foreground_color.blue << 8); |
| xr_textcolor.alpha = (gl_state.foreground_color.alpha << 8); |
| XftColorAllocValue( |
| x11_state.display, |
| x11_state.visual_info.visual, |
| x11_state.colormap_to_use, |
| &xr_textcolor, |
| &x11_state.xft_currentcolor |
| ); |
| #else /* Win32 */ |
| int win_linestyle, linewidth; |
| LOGBRUSH lb; |
| lb.lbStyle = BS_SOLID; |
| lb.lbColor = convert_to_win_color(gl_state.foreground_color); |
| lb.lbHatch = (LONG) NULL; |
| win_linestyle = win32_line_styles[gl_state.currentlinestyle]; |
| linewidth = max(gl_state.currentlinewidth, 1); // Win32 won't draw 0 width dashed lines. |
| |
| /* Delete previously used pen */ |
| if (!DeleteObject(win32_state.hGraphicsPen)) |
| WIN32_DELETE_ERROR(); |
| /* Create a new pen */ |
| win32_state.hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle | |
| PS_ENDCAP_FLAT, linewidth, &lb, (LONG) NULL, NULL); |
| if (!win32_state.hGraphicsPen) |
| WIN32_CREATE_ERROR(); |
| /* Select created pen into specified device context */ |
| if (!SelectObject(win32_state.hGraphicsDC, win32_state.hGraphicsPen)) |
| WIN32_SELECT_ERROR(); |
| |
| /* Delete previously used brush */ |
| if (!DeleteObject(win32_state.hGraphicsBrush)) |
| WIN32_DELETE_ERROR(); |
| /* Create a new brush */ |
| win32_state.hGraphicsBrush = CreateSolidBrush( |
| convert_to_win_color(gl_state.foreground_color) |
| ); |
| if (!win32_state.hGraphicsBrush) |
| WIN32_CREATE_ERROR(); |
| /* Select created brush into specified device context */ |
| if (!SelectObject(win32_state.hGraphicsDC, win32_state.hGraphicsBrush)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| } else { |
| auto color_index = std::find( |
| t_color::predef_colors.begin(), |
| t_color::predef_colors.end(), |
| gl_state.foreground_color |
| ); |
| if (color_index != t_color::predef_colors.end()) { |
| fprintf(gl_state.ps, "%s\n", ps_cnames[color_index - t_color::predef_colors.begin()]); |
| } else { |
| fprintf( |
| gl_state.ps, |
| "%f %f %f setrgbcolor\n", |
| gl_state.foreground_color.red / static_cast<float>(0xFF), |
| gl_state.foreground_color.green / static_cast<float>(0xFF), |
| gl_state.foreground_color.blue / static_cast<float>(0xFF) |
| ); |
| } |
| } |
| } |
| |
| /* Sets the current graphics context colour to cindex if it differs from the old colour */ |
| void setcolor(int cindex) { |
| if (gl_state.foreground_color != t_color::predef_colors[cindex]) { |
| force_setcolor(cindex); |
| } |
| } |
| |
| void setcolor(const t_color& color) { |
| if (gl_state.foreground_color != color) { |
| force_setcolor(color); |
| } |
| } |
| |
| void setcolor(uint_fast8_t r, uint_fast8_t g, uint_fast8_t b, uint_fast8_t a) { |
| setcolor(t_color(r, g, b, a)); |
| } |
| |
| /* Sets the current graphics context color to the index that corresponds to the |
| * string name passed in. Slower, but maybe more convenient for simple |
| * client code. |
| */ |
| void setcolor_by_name(string cname) { |
| int icolor = -1; |
| for (int i = 0; i < NUM_COLOR; i++) { |
| if (cname == ps_cnames[i]) { |
| icolor = i; |
| break; |
| } |
| } |
| if (icolor == -1) { |
| cout << "Error: unknown color " << cname << endl; |
| } else { |
| setcolor(icolor); |
| } |
| } |
| |
| t_color getcolor() { |
| return gl_state.foreground_color; |
| } |
| |
| /* Sets the current linestyle to linestyle in the graphics context. |
| * Note SOLID is 0 and DASHED is 1 for linestyle. |
| * capstyle can be 0 (butt), or 1 (round) |
| */ |
| static void force_setlinestyle(int linestyle, int capstyle) { |
| gl_state.currentlinestyle = linestyle; |
| gl_state.currentlinecap = capstyle; |
| |
| if (gl_state.disp_type == SCREEN) { |
| |
| #ifdef X11 |
| // XLIB DASH AND CAP |
| static int x_vals[2] = {LineSolid, LineOnOffDash}; |
| XSetLineAttributes(x11_state.display, x11_state.current_gc, gl_state.currentlinewidth, |
| x_vals[linestyle], capstyle == 0 ? CapButt : CapRound, JoinMiter); |
| |
| // CAIRO DASH |
| static double dashes[] = { |
| 5.0, /* ink */ |
| 3.0, /* skip */ |
| }; |
| if (linestyle == 1) |
| { |
| cairo_set_dash(x11_state.ctx, dashes, 2, 0); |
| } |
| else |
| { |
| cairo_set_dash(x11_state.ctx, dashes, 0, 0); |
| } |
| // CAIRO CAP |
| if (capstyle == 0) |
| { |
| cairo_set_line_cap(x11_state.ctx, CAIRO_LINE_CAP_BUTT); |
| } |
| else |
| { |
| cairo_set_line_cap(x11_state.ctx, CAIRO_LINE_CAP_ROUND); |
| } |
| #else // Win32 |
| LOGBRUSH lb; |
| lb.lbStyle = BS_SOLID; |
| lb.lbColor = convert_to_win_color(gl_state.foreground_color); |
| lb.lbHatch = (LONG) NULL; |
| int win_linestyle = win32_line_styles[linestyle]; |
| /* Win32 won't draw 0 width dashed lines. */ |
| int linewidth = max(gl_state.currentlinewidth, 1); |
| |
| /* Delete previously used pen */ |
| if (!DeleteObject(win32_state.hGraphicsPen)) |
| WIN32_DELETE_ERROR(); |
| /* Create a new pen */ |
| win32_state.hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle | |
| PS_ENDCAP_FLAT, linewidth, &lb, (LONG) NULL, NULL); |
| if (!win32_state.hGraphicsPen) |
| WIN32_CREATE_ERROR(); |
| /* Select created pen into specified device context */ |
| if (!SelectObject(win32_state.hGraphicsDC, win32_state.hGraphicsPen)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| } else { |
| if (linestyle == SOLID) |
| fprintf(gl_state.ps, "linesolid\n"); |
| else if (linestyle == DASHED) |
| fprintf(gl_state.ps, "linedashed\n"); |
| else { |
| printf("Error: invalid linestyle: %d\n", linestyle); |
| exit(1); |
| } |
| } |
| } |
| |
| /* Change the linestyle in the graphics context only if it differs from the current |
| * linestyle. |
| */ |
| void setlinestyle(int linestyle, int capstyle) { |
| if (linestyle != gl_state.currentlinestyle || capstyle != gl_state.currentlinecap) |
| force_setlinestyle(linestyle, capstyle); |
| } |
| |
| /* Sets current linewidth in the graphics context. |
| * linewidth should be greater than or equal to 0 to make any sense. |
| */ |
| static void force_setlinewidth(int linewidth) { |
| gl_state.currentlinewidth = linewidth; |
| |
| if (gl_state.disp_type == SCREEN) { |
| |
| #ifdef X11 |
| // X11 |
| static int x_vals[2] = {LineSolid, LineOnOffDash}; |
| XSetLineAttributes(x11_state.display, x11_state.current_gc, linewidth, |
| x_vals[gl_state.currentlinestyle], gl_state.currentlinecap == 0 ? CapButt : CapRound, JoinMiter); |
| |
| // CAIRO |
| cairo_set_line_width(x11_state.ctx, linewidth); |
| #else /* Win32 */ |
| LOGBRUSH lb; |
| lb.lbStyle = BS_SOLID; |
| lb.lbColor = convert_to_win_color(gl_state.foreground_color); |
| lb.lbHatch = (LONG) NULL; |
| int win_linestyle = win32_line_styles[gl_state.currentlinestyle]; |
| |
| if (linewidth == 0) |
| linewidth = 1; // Win32 won't draw dashed 0 width lines. |
| |
| /* Delete previously used pen */ |
| if (!DeleteObject(win32_state.hGraphicsPen)) |
| WIN32_DELETE_ERROR(); |
| /* Create a new pen */ |
| win32_state.hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle | |
| PS_ENDCAP_FLAT, linewidth, &lb, (LONG) NULL, NULL); |
| if (!win32_state.hGraphicsPen) |
| WIN32_CREATE_ERROR(); |
| /* Select created pen into specified device context */ |
| if (!SelectObject(win32_state.hGraphicsDC, win32_state.hGraphicsPen)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| } else { |
| fprintf(gl_state.ps, "%d setlinewidth\n", linewidth); |
| } |
| } |
| |
| /* Sets the linewidth in the grahpics context, if it differs from the current value. |
| */ |
| void setlinewidth(int linewidth) { |
| if (linewidth != gl_state.currentlinewidth) |
| force_setlinewidth(linewidth); |
| } |
| |
| /* Force the selected fontsize to be applied to the graphics context, |
| * whether or not it appears to match the current fontsize. This is necessary |
| * when switching between postscript and window out, for example. |
| * Valid point sizes are between 1 and MAX_FONT_SIZE. |
| */ |
| static void force_settextattrs(int pointsize, int degrees) { |
| |
| if (pointsize < 1) |
| pointsize = 1; |
| |
| gl_state.currentfontsize = pointsize; |
| gl_state.currentfontrotation = degrees; |
| |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef WIN32 |
| if (win32_state.hGraphicsFont != NULL) { |
| /* Delete previously used font */ |
| if (!DeleteObject(win32_state.hGraphicsFont)) { |
| WIN32_DELETE_ERROR(); |
| } |
| } |
| win32_state.hGraphicsFont = NULL; // expected by drawtext(..) |
| #endif |
| } else { |
| fprintf(gl_state.ps, "%d setfontsize\n", pointsize); |
| } |
| } |
| |
| /* For efficiency, these routines doesn't do anything if no change is |
| * implied. If you want to force the graphics context or PS file |
| * to have font info set, call force_settextattrs (this is necessary |
| * in initialization and screen / Postscript switches). |
| */ |
| void setfontsize(int pointsize) { |
| if (pointsize != gl_state.currentfontsize) { |
| force_settextattrs(pointsize, gl_state.currentfontrotation); |
| } |
| } |
| |
| int getfontsize() { |
| return gl_state.currentfontsize; |
| } |
| |
| void settextrotation(int degrees) { |
| if (degrees != gl_state.currentfontrotation) { |
| force_settextattrs(gl_state.currentfontsize, degrees); |
| } |
| } |
| |
| int gettextrotation() { |
| return gl_state.currentfontrotation; |
| } |
| |
| void settextattrs(int pointsize, int degrees) { |
| if (degrees != gl_state.currentfontrotation |
| || pointsize != gl_state.currentfontsize) { |
| force_settextattrs(pointsize, degrees); |
| } |
| } |
| |
| /* Puts a triangle in the poly array for button[bnum]. Haven't made this work for |
| * win32 yet and instead put "U", "D" excetra on the arrow buttons. |
| * VB To-do: make work for win32 someday. |
| */ |
| #ifdef X11 |
| |
| static void setpoly(int bnum, int xc, int yc, int r, float theta) { |
| int i; |
| |
| button_state.button[bnum].type = BUTTON_POLY; |
| for (i = 0; i < 3; i++) { |
| button_state.button[bnum].poly[i][0] = xc + r * cos(theta) + 0.5; |
| button_state.button[bnum].poly[i][1] = yc + r * sin(theta) + 0.5; |
| theta += 2. * PI / 3.; |
| } |
| } |
| #endif // X11 |
| |
| /* Maps a button onto the screen and set it up for input, etc. */ |
| static void map_button(int bnum) { |
| button_state.button[bnum].ispressed = 0; |
| |
| if (button_state.button[bnum].type != BUTTON_SEPARATOR) { |
| #ifdef X11 |
| button_state.button[bnum].win = XCreateSimpleWindow( |
| x11_state.display, x11_state.menu, |
| button_state.button[bnum].xleft, |
| button_state.button[bnum].ytop, |
| button_state.button[bnum].width, |
| button_state.button[bnum].height, 0, |
| x11_convert_to_xcolor(t_color::predef_colors[WHITE]), |
| x11_convert_to_xcolor(t_color::predef_colors[LIGHTGREY]) |
| ); |
| |
| XMapWindow(x11_state.display, button_state.button[bnum].win); |
| XSelectInput(x11_state.display, button_state.button[bnum].win, ButtonPressMask); |
| button_state.button[bnum].draw = XftDrawCreate( |
| x11_state.display, |
| button_state.button[bnum].win, |
| x11_state.visual_info.visual, |
| x11_state.colormap_to_use |
| ); |
| #else |
| wchar_t* WIN32_wchar_button_text = new wchar_t[BUTTON_TEXT_LEN]; |
| MultiByteToWideChar(CP_UTF8, 0, button_state.button[bnum].text, -1, |
| WIN32_wchar_button_text, BUTTON_TEXT_LEN); |
| button_state.button[bnum].hwnd = CreateWindowW( |
| L"button", |
| WIN32_wchar_button_text, |
| WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, |
| button_state.button[bnum].xleft, |
| button_state.button[bnum].ytop, |
| button_state.button[bnum].width, |
| button_state.button[bnum].height, |
| win32_state.hButtonsWnd, (HMENU) (200 + bnum), |
| (HINSTANCE) GetWindowLong(win32_state.hMainWnd, GWL_HINSTANCE), |
| NULL |
| ); |
| |
| delete[] WIN32_wchar_button_text; |
| |
| if (!InvalidateRect(win32_state.hButtonsWnd, NULL, TRUE)) |
| WIN32_DRAW_ERROR(); |
| if (!UpdateWindow(win32_state.hButtonsWnd)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } else { // Separator, not a button. |
| #ifdef X11 |
| button_state.button[bnum].win = -1; |
| #else // WIN32 |
| button_state.button[bnum].hwnd = NULL; |
| if (!InvalidateRect(win32_state.hButtonsWnd, NULL, TRUE)) |
| WIN32_DRAW_ERROR(); |
| if (!UpdateWindow(win32_state.hButtonsWnd)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } |
| } |
| |
| static void unmap_button(int bnum) { |
| /* Unmaps (removes) a button from the screen. */ |
| if (button_state.button[bnum].type != BUTTON_SEPARATOR) { |
| #ifdef X11 |
| XUnmapWindow(x11_state.display, button_state.button[bnum].win); |
| XftDrawDestroy(button_state.button[bnum].draw); |
| #else |
| if (!DestroyWindow(button_state.button[bnum].hwnd)) |
| WIN32_DRAW_ERROR(); |
| if (!InvalidateRect(win32_state.hButtonsWnd, NULL, TRUE)) |
| WIN32_DRAW_ERROR(); |
| if (!UpdateWindow(win32_state.hButtonsWnd)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } |
| } |
| |
| /* Creates a new button below the button containing prev_button_text. * |
| * The text and button function are set according to button_text and * |
| * button_func, respectively. */ |
| void create_button(const char *prev_button_text, const char *button_text, |
| void (*button_func) (void (*drawscreen) (void))) { |
| int i, bnum, space, bheight; |
| t_button_type button_type = BUTTON_TEXT; |
| |
| |
| space = 8; |
| |
| /* Only allow new buttons that are text or separator (not poly) types. |
| * They can also only go after buttons that are text buttons. |
| */ |
| |
| bnum = -1; |
| |
| for (i = 0; i < button_state.num_buttons; i++) { |
| if (button_state.button[i].type == BUTTON_TEXT && |
| strcmp(button_state.button[i].text, prev_button_text) == 0) { |
| bnum = i + 1; |
| break; |
| } |
| } |
| |
| if (bnum == -1) { |
| printf("Error in create_button: button with text %s not found.\n", |
| prev_button_text); |
| exit(1); |
| } |
| |
| button_state.button = static_cast<t_button *> (my_realloc(button_state.button, |
| (button_state.num_buttons + 1) * sizeof (t_button))); |
| /* NB: Requirement that you specify the button that this button goes under * |
| * guarantees that button[num_buttons-2] exists and is a text button. */ |
| |
| /* Special string to make a separator. */ |
| if (!strncmp(button_text, "---", 3)) { |
| bheight = 2; |
| button_type = BUTTON_SEPARATOR; |
| } else |
| bheight = 26; |
| |
| for (i = button_state.num_buttons; i > bnum; i--) { |
| button_state.button[i].xleft = button_state.button[i - 1].xleft; |
| button_state.button[i].ytop = button_state.button[i - 1].ytop + bheight + space; |
| button_state.button[i].height = button_state.button[i - 1].height; |
| button_state.button[i].width = button_state.button[i - 1].width; |
| button_state.button[i].type = button_state.button[i - 1].type; |
| strcpy(button_state.button[i].text, button_state.button[i - 1].text); |
| button_state.button[i].fcn = button_state.button[i - 1].fcn; |
| button_state.button[i].ispressed = button_state.button[i - 1].ispressed; |
| button_state.button[i].enabled = button_state.button[i - 1].enabled; |
| unmap_button(i - 1); |
| } |
| |
| i = bnum; |
| button_state.button[i].xleft = 6; |
| button_state.button[i].ytop = button_state.button[i - 1].ytop + button_state.button[i - 1].height |
| + space; |
| button_state.button[i].height = bheight; |
| button_state.button[i].width = 90; |
| button_state.button[i].type = button_type; |
| strncpy(button_state.button[i].text, button_text, BUTTON_TEXT_LEN); |
| button_state.button[i].text[BUTTON_TEXT_LEN-1] = '\0'; //Ensure null termination |
| button_state.button[i].fcn = button_func; |
| button_state.button[i].ispressed = false; |
| button_state.button[i].enabled = true; |
| |
| button_state.num_buttons++; |
| |
| for (; i < button_state.num_buttons; i++) |
| map_button(i); |
| } |
| |
| /* Destroys the button with text button_text. */ |
| void |
| destroy_button(const char *button_text) { |
| int i, bnum, space, bheight; |
| |
| bnum = -1; |
| space = 8; |
| for (i = 0; i < button_state.num_buttons; i++) { |
| if (button_state.button[i].type == BUTTON_TEXT && |
| strcmp(button_state.button[i].text, button_text) == 0) { |
| bnum = i; |
| break; |
| } |
| } |
| |
| if (bnum == -1) { |
| printf("Error in destroy_button: button with text %s not found.\n", |
| button_text); |
| exit(1); |
| } |
| |
| bheight = button_state.button[bnum].height; |
| |
| unmap_button(bnum); |
| for (i = bnum + 1; i < button_state.num_buttons; i++) { |
| button_state.button[i - 1].xleft = button_state.button[i].xleft; |
| button_state.button[i - 1].ytop = button_state.button[i].ytop - bheight - space; |
| button_state.button[i - 1].height = button_state.button[i].height; |
| button_state.button[i - 1].width = button_state.button[i].width; |
| button_state.button[i - 1].type = button_state.button[i].type; |
| strcpy(button_state.button[i - 1].text, button_state.button[i].text); |
| button_state.button[i - 1].fcn = button_state.button[i].fcn; |
| button_state.button[i - 1].ispressed = button_state.button[i].ispressed; |
| button_state.button[i - 1].enabled = button_state.button[i].enabled; |
| unmap_button(i); |
| } |
| |
| button_state.button = static_cast<t_button *> (my_realloc(button_state.button, |
| (button_state.num_buttons - 1) * sizeof (t_button))); |
| |
| button_state.num_buttons--; |
| |
| for (i = bnum; i < button_state.num_buttons; i++) |
| map_button(i); |
| } |
| |
| /* Open the toplevel window, get the colors, 2 graphics * |
| * contexts, load a font, and set up the toplevel window * |
| * Calls build_default_menu to set up the default menu. */ |
| void |
| init_graphics(const std::string& window_name, int cindex) { |
| init_graphics(window_name, t_color::predef_colors[cindex]); |
| } |
| |
| |
| void init_graphics(const std::string& window_name, const t_color& background) { |
| if (gl_state.initialized) // Singleton graphics. |
| return; |
| |
| reset_common_state(); |
| |
| gl_state.disp_type = SCREEN; |
| gl_state.background_color = background; |
| |
| // The Win32 and X11 APIs use C-strings. |
| const char* cstring_window_name = window_name.c_str(); |
| |
| #ifdef X11 |
| x11_init_graphics(cstring_window_name); |
| #else /* WIN32 */ |
| win32_init_graphics(cstring_window_name); |
| #endif |
| |
| gl_state.initialized = true; |
| } |
| |
| static void reset_common_state() { |
| gl_state.foreground_color = t_color::predef_colors[BLACK]; |
| gl_state.currentlinestyle = SOLID; |
| gl_state.currentlinewidth = 0; |
| gl_state.currentfontsize = 12; |
| gl_state.currentfontrotation = 0; |
| gl_state.current_draw_to = ON_SCREEN; |
| gl_state.current_draw_mode = DRAW_NORMAL; |
| gl_state.font_info.clear(); |
| gl_state.ProceedPressed = false; |
| gl_state.get_keypress_input = false; |
| gl_state.get_mouse_move_input = false; |
| gl_state.redraw_needed = false; |
| |
| #ifdef WIN32 |
| win32_reset_state(); |
| #endif |
| } |
| |
| |
| static void |
| update_transform(void) { |
| /* Set up the factors for transforming from the user world to X/Win32 screen |
| * (pixel) coordinates. */ |
| |
| float mult, diff, y1, y2, x1, x2; |
| |
| /* X Window coordinates go from (0,0) to (width-1,height-1) */ |
| diff = trans_coord.xright - trans_coord.xleft; |
| trans_coord.wtos_xmult = (trans_coord.top_width - 1 - MWIDTH) / diff; |
| diff = trans_coord.ybot - trans_coord.ytop; |
| trans_coord.wtos_ymult = (trans_coord.top_height - 1 - T_AREA_HEIGHT) / diff; |
| |
| /* Need to use same scaling factor to preserve aspect ratio */ |
| if (fabs(trans_coord.wtos_xmult) <= fabs(trans_coord.wtos_ymult)) { |
| mult = fabs(trans_coord.wtos_ymult / trans_coord.wtos_xmult); |
| y1 = trans_coord.ytop - (trans_coord.ybot - trans_coord.ytop)*(mult - 1) / 2; |
| y2 = trans_coord.ybot + (trans_coord.ybot - trans_coord.ytop)*(mult - 1) / 2; |
| trans_coord.ytop = y1; |
| trans_coord.ybot = y2; |
| } else { |
| mult = fabs(trans_coord.wtos_xmult / trans_coord.wtos_ymult); |
| x1 = trans_coord.xleft - (trans_coord.xright - trans_coord.xleft)*(mult - 1) / 2; |
| x2 = trans_coord.xright + (trans_coord.xright - trans_coord.xleft)*(mult - 1) / 2; |
| trans_coord.xleft = x1; |
| trans_coord.xright = x2; |
| } |
| diff = trans_coord.xright - trans_coord.xleft; |
| trans_coord.wtos_xmult = (trans_coord.top_width - 1 - MWIDTH) / diff; |
| diff = trans_coord.ybot - trans_coord.ytop; |
| trans_coord.wtos_ymult = (trans_coord.top_height - 1 - T_AREA_HEIGHT) / diff; |
| |
| trans_coord.stow_xmult = 1 / trans_coord.wtos_xmult; |
| trans_coord.stow_ymult = 1 / trans_coord.wtos_ymult; |
| } |
| |
| |
| static void |
| update_ps_transform(void) { |
| |
| /* Postscript coordinates start at (0,0) for the lower left hand corner * |
| * of the page and increase upwards and to the right. For 8.5 x 11 * |
| * sheet, coordinates go from (0,0) to (612,792). Spacing is 1/72 inch.* |
| * I'm leaving a minimum of half an inch (36 units) of border around * |
| * each edge. */ |
| |
| float mult, ps_width, ps_height; |
| |
| ps_width = 540.; /* 72 * 7.5 */ |
| ps_height = 720.; /* 72 * 10 */ |
| |
| trans_coord.ps_xmult = ps_width / (trans_coord.xright - trans_coord.xleft); |
| trans_coord.ps_ymult = ps_height / (trans_coord.ytop - trans_coord.ybot); |
| /* Need to use same scaling factor to preserve aspect ratio. * |
| * I show exactly as much on paper as the screen window shows, * |
| * or the user specifies. */ |
| if (fabs(trans_coord.ps_xmult) <= fabs(trans_coord.ps_ymult)) { |
| trans_coord.ps_left = 36.; |
| trans_coord.ps_right = 36. + ps_width; |
| mult = fabs(trans_coord.ps_xmult * (trans_coord.ytop - trans_coord.ybot)); |
| trans_coord.ps_bot = 396. - mult / 2.; |
| mult = fabs(trans_coord.ps_xmult * (trans_coord.ytop - trans_coord.ybot)); |
| trans_coord.ps_top = 396. + mult / 2.; |
| /* Maintain aspect ratio but watch signs */ |
| trans_coord.ps_ymult = (trans_coord.ps_xmult * trans_coord.ps_ymult < 0) ? |
| -trans_coord.ps_xmult : trans_coord.ps_xmult; |
| } else { |
| trans_coord.ps_bot = 36.; |
| trans_coord.ps_top = 36. + ps_height; |
| mult = fabs(trans_coord.ps_ymult * (trans_coord.xright - trans_coord.xleft)); |
| trans_coord.ps_left = 306. - mult / 2.; |
| mult = fabs(trans_coord.ps_ymult * (trans_coord.xright - trans_coord.xleft)); |
| trans_coord.ps_right = 306. + mult / 2.; |
| /* Maintain aspect ratio but watch signs */ |
| trans_coord.ps_xmult = (trans_coord.ps_xmult * trans_coord.ps_ymult < 0) ? |
| -trans_coord.ps_ymult : trans_coord.ps_ymult; |
| } |
| |
| // /The user can also draw in screen coordinates, so we also need a |
| // transformation from screen coordinates to postscript. |
| trans_coord.s_to_ps_xmult = trans_coord.ps_xmult * trans_coord.stow_xmult; |
| trans_coord.s_to_ps_ymult = trans_coord.ps_ymult * trans_coord.stow_ymult; |
| } |
| |
| |
| static bool is_droppable_event( |
| #ifdef X11 |
| XEvent* event |
| #elif defined WIN32 |
| MSG* message |
| #endif |
| ) { |
| #ifdef X11 |
| return |
| event->type == MotionNotify || ( |
| event->type == ButtonPress && ( |
| event->xbutton.button == Button4 || |
| event->xbutton.button == Button5 |
| ) |
| ); |
| #elif defined WIN32 |
| return message->message == WM_MOUSEWHEEL |
| || message->message == WM_MOUSEMOVE; |
| #endif |
| } |
| |
| /* The program's main event loop. Must be passed a user routine |
| * drawscreen which redraws the screen. It handles all window resizing |
| * zooming etc. itself. |
| * The three other callbacks are optional -- they can be NULL if you don't |
| * want to use them in your program. If non-NULL, they are called when the user |
| * clicks a mouse button, moves the mouse or presses the keyboard while the |
| * cursor is in the graphics area. |
| */ |
| void |
| event_loop(void (*act_on_mousebutton)(float x, float y, t_event_buttonPressed button_info), |
| void (*act_on_mousemove)(float x, float y), |
| void (*act_on_keypress)(char key_pressed, int keysym), |
| void (*drawscreen) (void)) { |
| |
| if (drawscreen == NULL) { |
| cerr << "Error in event_loop: drawscreen is NULL, but it is a mandatory callback.\n"; |
| exit(1); |
| } |
| |
| // The following settings are useful for automarking in courses. |
| if (gl_state.redirect_to_postscript) |
| postscript (drawscreen); |
| |
| if (gl_state.disable_event_loop) // Avoids hanging the automarker when students call the event_loop |
| return; |
| |
| #ifdef X11 |
| x11_event_loop(act_on_mousebutton, act_on_mousemove, act_on_keypress, drawscreen); |
| #else /* Win32 */ |
| |
| // For event handling with Win32, need to set these file scope function |
| // pointers, then dispatch the event and get called via callbacks from |
| // Windows (eg. WIN32_GraphicsWND()). Actual event handling is done |
| // in these Win32-specific callback functions. |
| |
| MSG msg; |
| |
| win32_mouseclick_ptr = act_on_mousebutton; |
| win32_mousemove_ptr = act_on_mousemove; |
| win32_keypress_ptr = act_on_keypress; |
| win32_drawscreen_ptr = drawscreen; |
| gl_state.ProceedPressed = false; |
| win32_state.InEventLoop = true; |
| |
| win32_invalidate_screen(); |
| |
| // timout timer, for event loop. Supposed to be 2 seconds, but isn't? |
| // UINT_PTR timeout_timer = SetTimer(NULL, NULL, 2000, NULL); |
| bool dropped_something = false; |
| |
| // Windows event dropping explanation: |
| // like X11, it will drop events which match is_droppable_event(...), and |
| // are followed by another event which matches is_droppable_event(...). |
| // However, there is no way to sync events with the system; no way to make |
| // sure that this application has all pending events in it's queue to |
| // process. So, hopefully this doesn't cause any problems. Also, due to the |
| // fact that windows groups multiple fast scrolls into one message, there |
| // is logic in win32_handle_mousewheel_zooming to address this. |
| while (!gl_state.ProceedPressed && GetMessage(&msg, NULL, 0, 0)) { |
| TranslateMessage(&msg); |
| if (msg.message == WM_CHAR) { // only the top window can get keyboard events |
| msg.hwnd = win32_state.hMainWnd; |
| } |
| if (is_droppable_event(&msg)) { |
| MSG next_msg; |
| if (PeekMessage(&next_msg, NULL, 0, 0, PM_NOREMOVE)) { |
| // if the current event is droppable, check if |
| // if the next event is droppable. |
| if (is_droppable_event(&next_msg)) { |
| // if both droppable, skip the event. |
| if (msg.message == WM_MOUSEWHEEL) { |
| // if dropping scroll event, do the scroll, but don't redraw. |
| win32_handle_mousewheel_zooming(msg.wParam, msg.lParam, false); |
| } |
| dropped_something = true; |
| continue; |
| } |
| } |
| } |
| DispatchMessage(&msg); |
| } |
| win32_state.InEventLoop = false; |
| #endif |
| } |
| |
| void |
| clearscreen(void) { |
| t_color savecolor; |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef X11 |
| if (x11_state.draw_area == &(x11_state.toplevel)) { |
| XClearWindow(x11_state.display, x11_state.toplevel); |
| } |
| else { |
| setcolor(gl_state.background_color); |
| XFillRectangle(x11_state.display, x11_state.draw_buffer, x11_state.current_gc, 0, 0, |
| x11_state.attributes.width, x11_state.attributes.height); |
| } |
| #else /* Win32 */ |
| savecolor = gl_state.foreground_color; |
| setcolor(gl_state.background_color); |
| fillrect(trans_coord.xleft, trans_coord.ytop, |
| trans_coord.xright, trans_coord.ybot); |
| setcolor(savecolor); |
| #endif |
| } else { // Postscript |
| /* erases current page. Don't use erasepage, since this will erase * |
| * everything, (even stuff outside the clipping path) causing * |
| * problems if this picture is incorporated into a larger document. */ |
| savecolor = gl_state.foreground_color; |
| setcolor(gl_state.background_color); |
| fprintf(gl_state.ps, "clippath fill\n\n"); |
| setcolor(savecolor); |
| } |
| } |
| |
| /* Return 1 if I can quarantee no part of this rectangle will * |
| * lie within the user drawing area. Otherwise return 0. * |
| * Note: this routine is only used to help speed (and to shrink ps * |
| * files) -- it will be highly effective when the graphics are zoomed * |
| * in and lots are off-screen. I don't have to pre-clip for * |
| * correctness. */ |
| static int |
| rect_off_screen(float x1, float y1, float x2, float y2) { |
| // No pre-clip if you're in screen coordinates; user should know where he/she is |
| // drawing that case so not necessary to pre-clip for speed. |
| if (gl_state.currentcoordinatesystem == GL_SCREEN) |
| return 0; |
| |
| float xmin, xmax, ymin, ymax; |
| |
| xmin = min(trans_coord.xleft, trans_coord.xright); |
| if (x1 < xmin && x2 < xmin) |
| return (1); |
| |
| xmax = max(trans_coord.xleft, trans_coord.xright); |
| if (x1 > xmax && x2 > xmax) |
| return (1); |
| |
| ymin = min(trans_coord.ytop, trans_coord.ybot); |
| if (y1 < ymin && y2 < ymin) |
| return (1); |
| |
| ymax = max(trans_coord.ytop, trans_coord.ybot); |
| if (y1 > ymax && y2 > ymax) |
| return (1); |
| |
| return (0); |
| } |
| |
| static int rect_off_screen(const t_bound_box& bbox) { |
| return rect_off_screen(bbox.left(), bbox.bottom(), bbox.right(), bbox.top()); |
| } |
| |
| t_bound_box get_visible_world() { |
| return t_bound_box( |
| trans_coord.xleft, |
| trans_coord.ybot, |
| trans_coord.xright, |
| trans_coord.ytop |
| ); |
| } |
| |
| /* Returns the limits of the user-drawable window (graphics area) in screen |
| * coordinates. |
| */ |
| t_bound_box get_visible_screen() { |
| // Screen coordinates are (0,0) at the top left. Top level window goes from |
| // 0 to top_width-1 or top_height - 1. |
| return (t_bound_box ( |
| 0, trans_coord.top_height - 1 - T_AREA_HEIGHT, |
| trans_coord.top_width - 1 - MWIDTH, 0) |
| ); |
| } |
| |
| |
| bool LOD_screen_area_test(t_bound_box test, float screen_area_threshold) { |
| return world_to_scrn(test).area() > screen_area_threshold; |
| } |
| |
| bool LOD_min_dim_test(float dim_threshold) { |
| t_bound_box vis_world = get_visible_world(); |
| //printf("visible world width = %f, height = %f",vis_world.get_width(),vis_world.get_height()); |
| return |
| ( |
| (abs(vis_world.get_height()) < abs(vis_world.get_width())) ? |
| abs(vis_world.get_height()) : abs(vis_world.get_width()) |
| ) < dim_threshold; |
| } |
| |
| void drawline(const t_point& p1, const t_point& p2) { |
| drawline(p1.x, p1.y, p2.x, p2.y); |
| } |
| |
| void |
| drawline(float x1, float y1, float x2, float y2) { |
| /* Draw a line from (x1,y1) to (x2,y2) in the user-drawable area. * |
| * Coordinates are in world (user) space. */ |
| |
| /* Pre-clipping has been tested on both Windows and Linux, and it was found to be useful * |
| * for speeding up drawscreen() runtime. */ |
| if (rect_off_screen(x1, y1, x2, y2)) |
| return; |
| |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef X11 |
| /* Xlib.h prototype has x2 and y1 mixed up. */ |
| if (use_cairo()) |
| { |
| cairo_move_to(x11_state.ctx, xworld_to_scrn(x1), yworld_to_scrn(y1)); |
| cairo_line_to(x11_state.ctx, xworld_to_scrn(x2), yworld_to_scrn(y2)); |
| cairo_stroke(x11_state.ctx); |
| } |
| else |
| { |
| XDrawLine(x11_state.display, *(x11_state.draw_area), x11_state.current_gc, xworld_to_scrn(x1), |
| yworld_to_scrn(y1), xworld_to_scrn(x2), yworld_to_scrn(y2)); |
| } |
| #else /* Win32 */ |
| if (!BeginPath(win32_state.hGraphicsDC)) |
| WIN32_DRAW_ERROR(); |
| if (!MoveToEx(win32_state.hGraphicsDC, xworld_to_scrn(x1), yworld_to_scrn(y1), NULL)) |
| WIN32_DRAW_ERROR(); |
| if (!LineTo(win32_state.hGraphicsDC, xworld_to_scrn(x2), yworld_to_scrn(y2))) |
| WIN32_DRAW_ERROR(); |
| if (!EndPath(win32_state.hGraphicsDC)) |
| WIN32_DRAW_ERROR(); |
| if (!StrokePath(win32_state.hGraphicsDC)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } else { |
| fprintf(gl_state.ps, "%.2f %.2f %.2f %.2f drawline\n", x_to_post(x1), y_to_post(y1), |
| x_to_post(x2), y_to_post(y2)); |
| } |
| } |
| |
| void drawrect(const t_bound_box& rect) { |
| drawrect(rect.bottom_left(), rect.top_right()); |
| } |
| |
| void drawrect(const t_point& bottomleft, const t_point& upperright) { |
| drawrect(bottomleft.x, bottomleft.y, upperright.x, upperright.y); |
| } |
| |
| /* (x1,y1) and (x2,y2) are diagonally opposed corners, in world coords. */ |
| void |
| drawrect(float x1, float y1, float x2, float y2) { |
| int xw1, yw1, xw2, yw2; |
| |
| if (rect_off_screen(x1, y1, x2, y2)) |
| return; |
| |
| if (gl_state.disp_type == SCREEN) { |
| /* translate to X Windows calling convention. */ |
| xw1 = xworld_to_scrn(x1); |
| xw2 = xworld_to_scrn(x2); |
| yw1 = yworld_to_scrn(y1); |
| yw2 = yworld_to_scrn(y2); |
| #ifdef X11 |
| unsigned int width, height; |
| int xl, yt; |
| |
| xl = min(xw1, xw2); |
| yt = min(yw1, yw2); |
| width = abs(xw1 - xw2); |
| height = abs(yw1 - yw2); |
| |
| if (use_cairo()) |
| { |
| cairo_rectangle(x11_state.ctx, xl, yt, width, height); |
| cairo_stroke(x11_state.ctx); |
| } |
| else |
| { |
| XDrawRectangle(x11_state.display, *(x11_state.draw_area), |
| x11_state.current_gc, xl, yt, width, height); |
| } |
| |
| #else /* Win32 */ |
| if (xw1 > xw2) { |
| int temp = xw1; |
| xw1 = xw2; |
| xw2 = temp; |
| } |
| if (yw1 > yw2) { |
| int temp = yw1; |
| yw1 = yw2; |
| yw2 = temp; |
| } |
| |
| HBRUSH hOldBrush; |
| |
| /* NULL_BRUSH is a Windows stock object which does not fill any space. Thus, a * |
| * rectangle can be drawn by outlining it with the current pen and not filling * |
| * it since NULL_BRUSH is used. Another alternative to drawing a rectangle is * |
| * to draw four lines individually. */ |
| hOldBrush = (HBRUSH) SelectObject(win32_state.hGraphicsDC, GetStockObject(NULL_BRUSH)); |
| if (!(hOldBrush)) |
| WIN32_SELECT_ERROR(); |
| |
| if (!Rectangle(win32_state.hGraphicsDC, xw1, yw1, xw2, yw2)) |
| WIN32_DRAW_ERROR(); |
| |
| /* Need to restore the previous brush into the specified device context after * |
| * the rectangle is drawn. */ |
| if (!SelectObject(win32_state.hGraphicsDC, hOldBrush)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| |
| } else { |
| fprintf(gl_state.ps, "%.2f %.2f %.2f %.2f drawrect\n", x_to_post(x1), y_to_post(y1), |
| x_to_post(x2), y_to_post(y2)); |
| } |
| } |
| |
| void fillrect(const t_bound_box& rect) { |
| fillrect(rect.bottom_left(), rect.top_right()); |
| } |
| |
| void fillrect(const t_point& bottomleft, const t_point& upperright) { |
| fillrect(bottomleft.x, bottomleft.y, upperright.x, upperright.y); |
| } |
| |
| /* (x1,y1) and (x2,y2) are diagonally opposed corners in world coords. */ |
| void |
| fillrect(float x1, float y1, float x2, float y2) { |
| int xw1, yw1, xw2, yw2; |
| |
| if (rect_off_screen(x1, y1, x2, y2)) |
| return; |
| |
| if (gl_state.disp_type == SCREEN) { |
| /* translate to X Windows calling convention. */ |
| xw1 = xworld_to_scrn(x1); |
| xw2 = xworld_to_scrn(x2); |
| yw1 = yworld_to_scrn(y1); |
| yw2 = yworld_to_scrn(y2); |
| #ifdef X11 |
| unsigned int width, height; |
| int xl, yt; |
| |
| xl = min(xw1, xw2); |
| yt = min(yw1, yw2); |
| width = abs(xw1 - xw2); |
| height = abs(yw1 - yw2); |
| |
| if (use_cairo()) |
| { |
| cairo_rectangle(x11_state.ctx, xl, yt, width, height); |
| cairo_fill(x11_state.ctx); |
| } |
| else |
| { |
| XFillRectangle(x11_state.display, *(x11_state.draw_area), |
| x11_state.current_gc, xl, yt, width, height); |
| } |
| #else /* Win32 */ |
| if (xw1 > xw2) { |
| int temp = xw1; |
| xw1 = xw2; |
| xw2 = temp; |
| } |
| if (yw1 > yw2) { |
| int temp = yw1; |
| yw1 = yw2; |
| yw2 = temp; |
| } |
| |
| if (!Rectangle(win32_state.hGraphicsDC, xw1, yw1, xw2, yw2)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } else { |
| fprintf(gl_state.ps, "%.2f %.2f %.2f %.2f fillrect\n", x_to_post(x1), |
| y_to_post(y1), x_to_post(x2), y_to_post(y2)); |
| } |
| } |
| |
| /* Normalizes an angle to be between 0 and 360 degrees. */ |
| static float |
| angnorm(float ang) { |
| int scale; |
| |
| if (ang < 0) { |
| scale = static_cast<int>(ang / 360. - 1); |
| } else { |
| scale = static_cast<int>(ang / 360.); |
| } |
| ang = ang - scale * 360; |
| return (ang); |
| } |
| |
| void drawellipticarc( |
| const t_point& center, float radx, float rady, float startang, float angextent) { |
| |
| drawellipticarc(center.x, center.y, radx, rady, startang, angextent); |
| } |
| |
| void |
| drawellipticarc(float xc, float yc, float radx, float rady, float startang, float angextent) { |
| int xl, yt; |
| unsigned int width, height; |
| |
| /* Conservative (but fast) clip test -- check containing rectangle of * |
| * an ellipse. */ |
| |
| if (rect_off_screen(xc - radx, yc - rady, xc + radx, yc + rady)) |
| return; |
| |
| /* X Windows has trouble with very large angles. (Over 360). * |
| * Do following to prevent its inaccurate (overflow?) problems. */ |
| if (fabs(angextent) > 360.) |
| angextent = 360.; |
| |
| startang = angnorm(startang); |
| |
| if (gl_state.disp_type == SCREEN) { |
| float screen_rad_x = xrad_to_scrn (radx); // Convert radx to pixels |
| float screen_rad_y = yrad_to_scrn (rady); // Convert rady to pixels |
| xl = static_cast<int>(xworld_to_scrn(xc) - screen_rad_x); |
| yt = static_cast<int>(yworld_to_scrn(yc) - screen_rad_y); |
| width = static_cast<unsigned int>(2 * screen_rad_x); |
| height = static_cast<unsigned int>(2 * screen_rad_y); |
| #ifdef X11 |
| XDrawArc(x11_state.display, *(x11_state.draw_area), x11_state.current_gc, xl, yt, width, height, |
| static_cast<int>(startang * 64), static_cast<int>(angextent * 64)); |
| #else // Win32 |
| int p1, p2, p3, p4; |
| |
| /* set arc direction */ |
| float startang_rad, endang_rad; |
| if (angextent > 0) { |
| startang_rad = (float) DEGTORAD(startang); |
| endang_rad = (float) DEGTORAD(startang + angextent - .001); |
| } else { |
| startang_rad = (float) DEGTORAD(startang + angextent + .001); |
| endang_rad = (float) DEGTORAD(startang); |
| } |
| p1 = (int) (xworld_to_scrn(xc) + screen_rad_x * cos(startang_rad)); |
| p2 = (int) (yworld_to_scrn(yc) - screen_rad_y * sin(startang_rad)); |
| p3 = (int) (xworld_to_scrn(xc) + screen_rad_x * cos(endang_rad)); |
| p4 = (int) (yworld_to_scrn(yc) - screen_rad_y * sin(endang_rad)); |
| |
| if (!Arc(win32_state.hGraphicsDC, xl, yt, xl + width, yt + height, p1, p2, p3, p4)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } else { |
| fprintf(gl_state.ps, "gsave\n"); |
| fprintf(gl_state.ps, "%.2f %.2f translate\n", x_to_post(xc), y_to_post(yc)); |
| fprintf(gl_state.ps, "%.2f 1 scale\n", |
| fabs(x_to_post(radx) - x_to_post(0)) / fabs(y_to_post(rady) - y_to_post(0))); |
| fprintf(gl_state.ps, "0 0 %.2f %.2f %.2f %s\n", |
| fabs(x_to_post(rady) - x_to_post(0)), startang, |
| startang + angextent, (angextent < 0) ? "drawarcn" : "drawarc"); |
| fprintf(gl_state.ps, "grestore\n"); |
| } |
| } |
| |
| /* Startang is relative to the Window's positive x direction. Angles in degrees. |
| */ |
| void |
| drawarc(float xc, float yc, float rad, float startang, |
| float angextent) { |
| drawellipticarc(xc, yc, rad, rad, startang, angextent); |
| } |
| |
| /* Fills a elliptic arc. Startang is relative to the Window's positive x |
| * direction. Angles in degrees. |
| */ |
| |
| void fillellipticarc( |
| const t_point& center, float radx, float rady, float startang, float angextent) { |
| |
| fillellipticarc(center.x, center.y, radx, rady, startang, angextent); |
| } |
| |
| void |
| fillellipticarc(float xc, float yc, float radx, float rady, float startang, |
| float angextent) { |
| int xl, yt; |
| unsigned int width, height; |
| |
| /* Conservative (but fast) clip test -- check containing rectangle of * |
| * a circle. */ |
| |
| if (rect_off_screen(xc - radx, yc - rady, xc + radx, yc + rady)) |
| return; |
| |
| /* X Windows has trouble with very large angles. (Over 360). * |
| * Do following to prevent its inaccurate (overflow?) problems. */ |
| |
| if (fabs(angextent) > 360.) |
| angextent = 360.; |
| |
| startang = angnorm(startang); |
| |
| if (gl_state.disp_type == SCREEN) { |
| float screen_rad_x = xrad_to_scrn (radx); // Convert radx to pixels |
| float screen_rad_y = yrad_to_scrn (rady); // Convert rady to pixels |
| xl = static_cast<int>(xworld_to_scrn(xc) - screen_rad_x); |
| yt = static_cast<int>(yworld_to_scrn(yc) - screen_rad_y); |
| width = static_cast<unsigned int>(2 * screen_rad_x); |
| height = static_cast<unsigned int>(2 * screen_rad_y); |
| #ifdef X11 |
| XFillArc(x11_state.display, *(x11_state.draw_area), x11_state.current_gc, xl, yt, width, height, |
| static_cast<int>(startang * 64), static_cast<int>(angextent * 64)); |
| #else // Win32 |
| HPEN hOldPen; |
| int p1, p2, p3, p4; |
| |
| /* set pie direction */ |
| float startang_rad, endang_rad; |
| if (angextent > 0) { |
| startang_rad = (float) DEGTORAD(startang); |
| endang_rad = (float) DEGTORAD(startang + angextent - .001); |
| } else { |
| startang_rad = (float) DEGTORAD(startang + angextent + .001); |
| endang_rad = (float) DEGTORAD(startang); |
| } |
| p1 = (int) (xworld_to_scrn(xc) + screen_rad_x * cos(startang_rad)); |
| p2 = (int) (yworld_to_scrn(yc) - screen_rad_y * sin(startang_rad)); |
| p3 = (int) (xworld_to_scrn(xc) + screen_rad_x * cos(endang_rad)); |
| p4 = (int) (yworld_to_scrn(yc) - screen_rad_y * sin(endang_rad)); |
| |
| /* NULL_PEN is a Windows stock object which does not draw anything. Set current * |
| * pen to NULL_PEN in order to fill the arc without drawing the outline. */ |
| hOldPen = (HPEN) SelectObject(win32_state.hGraphicsDC, GetStockObject(NULL_PEN)); |
| if (!(hOldPen)) |
| WIN32_SELECT_ERROR(); |
| |
| // Win32 API says a zero return value indicates an error, but it seems to always |
| // return zero. Don't check for an error on Pie. |
| Pie(win32_state.hGraphicsDC, xl, yt, xl + width, yt + height, p1, p2, p3, p4); |
| //if(!Pie(win32_state.hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4)); |
| //WIN32_DRAW_ERROR(); |
| |
| /* Need to restore the original pen into the device context after filling. */ |
| if (!SelectObject(win32_state.hGraphicsDC, hOldPen)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| } else { |
| fprintf(gl_state.ps, "gsave\n"); |
| fprintf(gl_state.ps, "%.2f %.2f translate\n", x_to_post(xc), y_to_post(yc)); |
| fprintf(gl_state.ps, "%.2f 1 scale\n", |
| fabs(x_to_post(radx) - x_to_post(0)) / fabs(y_to_post(rady) - y_to_post(0))); |
| fprintf(gl_state.ps, "%.2f %.2f %.2f 0 0 %s\n", |
| fabs(x_to_post(rady) - x_to_post(0)), startang, |
| startang + angextent, (angextent < 0) ? "fillarcn" : "fillarc"); |
| fprintf(gl_state.ps, "grestore\n"); |
| } |
| } |
| |
| void fillarc(const t_point& center, float rad, float startang, float angextent) { |
| fillellipticarc(center.x, center.y, rad, rad, startang, angextent); |
| } |
| |
| void fillarc(float xc, float yc, float rad, float startang, float angextent) { |
| fillellipticarc(xc, yc, rad, rad, startang, angextent); |
| } |
| |
| |
| // For speed, use a fixed size polygon point buffer when possible (no dynamic |
| // memory allocation). Dynamically allocate an arbitrary size buffer only when |
| // necessary. |
| #define MAX_FIXED_POLY_PTS 100 |
| |
| void |
| fillpoly(t_point *points, int npoints) { |
| float xmin, ymin, xmax, ymax; |
| |
| |
| /* Conservative (but fast) clip test -- check containing rectangle of * |
| * polygon. */ |
| |
| xmin = xmax = points[0].x; |
| ymin = ymax = points[0].y; |
| |
| for (int i = 1; i < npoints; i++) { |
| xmin = min(xmin, points[i].x); |
| xmax = max(xmax, points[i].x); |
| ymin = min(ymin, points[i].y); |
| ymax = max(ymax, points[i].y); |
| } |
| |
| if (rect_off_screen(xmin, ymin, xmax, ymax)) |
| return; |
| |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef X11 |
| #define MY_POINT XPoint |
| #else // WIN32 --> uses a different type but with same field names. |
| #define MY_POINT POINT |
| #endif |
| MY_POINT fixed_transpoints[MAX_FIXED_POLY_PTS]; |
| MY_POINT *transpoints = fixed_transpoints; |
| if (npoints > MAX_FIXED_POLY_PTS) { |
| transpoints = static_cast<MY_POINT *> (my_malloc(npoints * sizeof (MY_POINT))); |
| } |
| for (int i = 0; i < npoints; i++) { |
| transpoints[i].x = static_cast<long>(xworld_to_scrn(points[i].x)); |
| transpoints[i].y = static_cast<long>(yworld_to_scrn(points[i].y)); |
| } |
| #ifdef X11 |
| if (use_cairo()) |
| { |
| cairo_move_to(x11_state.ctx, transpoints[0].x, transpoints[0].y); |
| for (int i = 1; i < npoints; ++i) |
| { |
| cairo_line_to(x11_state.ctx, transpoints[i].x, transpoints[i].y); |
| } |
| cairo_close_path(x11_state.ctx); |
| cairo_fill(x11_state.ctx); |
| } |
| else |
| { |
| XFillPolygon(x11_state.display, *(x11_state.draw_area), x11_state.current_gc, |
| transpoints, npoints, Complex, CoordModeOrigin); |
| } |
| #else |
| HPEN hOldPen; |
| /* NULL_PEN is a Windows stock object which does not draw anything. Set current * |
| * pen to NULL_PEN in order to fill polygon without drawing its outline. */ |
| hOldPen = (HPEN) SelectObject(win32_state.hGraphicsDC, GetStockObject(NULL_PEN)); |
| if (!(hOldPen)) |
| WIN32_SELECT_ERROR(); |
| |
| if (!Polygon(win32_state.hGraphicsDC, transpoints, npoints)) |
| WIN32_DRAW_ERROR(); |
| |
| /* Need to restore the original pen into the device context after drawing. */ |
| if (!SelectObject(win32_state.hGraphicsDC, hOldPen)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| |
| if (npoints > MAX_FIXED_POLY_PTS) |
| free(transpoints); |
| } else { |
| fprintf(gl_state.ps, "\n"); |
| |
| for (int i = npoints - 1; i >= 0; i--) |
| fprintf(gl_state.ps, "%.2f %.2f\n", x_to_post(points[i].x), |
| y_to_post(points[i].y)); |
| |
| fprintf(gl_state.ps, "%d fillpoly\n", npoints); |
| } |
| } |
| |
| /** |
| * drawtext convenience functions |
| */ |
| |
| void drawtext_in(const t_bound_box& bbox, const std::string& text) { |
| drawtext(bbox.get_center(), text, bbox); |
| } |
| |
| void drawtext_in(const t_bound_box& bbox, const std::string& text, float tolerance) { |
| drawtext(bbox.get_center(), text, bbox, tolerance); |
| } |
| |
| void drawtext( |
| const t_point& text_center, const std::string& text, const t_bound_box& bounds, float tolerance) { |
| |
| t_point tolerance_pt(tolerance, tolerance); |
| t_bound_box tolerance_bounds = bounds; |
| |
| tolerance_bounds.bottom_left() -= tolerance_pt; |
| tolerance_bounds.top_right() += tolerance_pt; |
| |
| drawtext(text_center, text, tolerance_bounds); |
| } |
| |
| void drawtext(const t_point& text_center, const std::string& text, const t_bound_box& bounds) { |
| if (bounds.intersects(text_center) != true) { |
| cout << "The center of the text \"" << text << "\" isn't in its bounding box.\n"; |
| // printf("the center of the text \"%s\" isn't in its bounding box", text.c_str()); |
| } |
| t_point bottomleft_bounds = text_center - bounds.bottom_left(); |
| t_point topright_bounds = bounds.top_right() - text_center; |
| t_point min_bounds( |
| min(bottomleft_bounds.x, topright_bounds.x), |
| min(bottomleft_bounds.y, topright_bounds.y) |
| ); |
| min_bounds *= 2; |
| drawtext(text_center, text, min_bounds.x, min_bounds.y); |
| } |
| |
| void drawtext(const t_point& text_center, const std::string& text, float boundx, float boundy) { |
| drawtext(text_center.x, text_center.y, text, boundx, boundy); |
| } |
| |
| /** |
| * the real drawtext function. |
| * First, using a non rotated font (probably already loaded), it conservatively checks |
| * if an approximation of the bbox is on the screen, and not to large for the given |
| * bound{x,y}. Then it loads the proper (possibly rotated) font, and checks the real |
| * bounds of the text and performs the same checks. Then it draws the text to the |
| * XftDraw (X11) or screen (WIN32). boundx and boundy are in the current coordinate |
| * system (world or screen). |
| */ |
| void drawtext(float xc, float yc, const std::string& str_text, float boundx, float boundy) { |
| // Need a C-string to call the low-level (X11 or win32) apis. |
| const char* text = str_text.c_str(); |
| int text_byte_length = strlen(text); |
| float angle = PI * gl_state.currentfontrotation / 180.; |
| float abscos = fabs(cos(angle)); |
| float abssin = fabs(sin(angle)); |
| #ifdef WIN32 |
| wchar_t* WIN32_wchar_text = new wchar_t[text_byte_length + 1]; |
| size_t WIN32_wchar_text_len = |
| MultiByteToWideChar(CP_UTF8, 0, text, text_byte_length, WIN32_wchar_text, text_byte_length); |
| WIN32_wchar_text[text_byte_length] = 0; |
| #endif |
| |
| // exit early (conservatively) to prevent filling the font cache with fonts not visible |
| if (rect_off_screen(t_bound_box(t_point(xc - boundx / 2, yc - boundy / 2), boundx, boundy))) { |
| // if the largest bbox the text could have is off the screen, don't even load the font. |
| return; |
| } |
| |
| float trans_xmult, trans_ymult; |
| if (gl_state.currentcoordinatesystem == GL_WORLD) { |
| trans_xmult = trans_coord.stow_xmult; |
| trans_ymult = trans_coord.stow_ymult; |
| } |
| else { // Screen coordinates: clip bounding box passed in already in pixels. |
| trans_xmult = 1; |
| trans_ymult = 1; |
| } |
| // approximate width & height and check against bound{x,y} |
| font_ptr zero_font = gl_state.font_info.get_font_info(gl_state.currentfontsize, 0); |
| |
| #ifdef X11 |
| XGlyphInfo zero_extents; |
| XftTextExtentsUtf8( |
| x11_state.display, zero_font, reinterpret_cast<const FcChar8*>(text), text_byte_length, &zero_extents); |
| int zero_approx_height = zero_extents.height; |
| int zero_approx_width = zero_extents.width; |
| |
| #elif defined WIN32 |
| HFONT zero_hfont = CreateFontIndirect(zero_font); |
| if (!zero_hfont) { |
| WIN32_CREATE_ERROR(); |
| } |
| // Select zero font into specified device context |
| if (!SelectObject(win32_state.hGraphicsDC, zero_hfont)) { |
| WIN32_SELECT_ERROR(); |
| } |
| |
| SIZE textsize; |
| if (!GetTextExtentPoint32W( |
| win32_state.hGraphicsDC, |
| WIN32_wchar_text, |
| WIN32_wchar_text_len, |
| &textsize) |
| ) { |
| WIN32_DRAW_ERROR(); |
| } |
| |
| int zero_approx_width = textsize.cx; |
| int zero_approx_height = textsize.cy; |
| |
| // reselect normal font |
| if (win32_state.hGraphicsFont != NULL) { |
| if (!SelectObject(win32_state.hGraphicsDC, win32_state.hGraphicsFont)) { |
| WIN32_SELECT_ERROR(); |
| } |
| } |
| |
| // eventhough it might be selected, we wont't be drawing with it, because |
| // the HFONT in wit32_state is NULL, so it will be set before something |
| // tries to draw text. Putting it in a later, more proper, place in this |
| // function causes an draw error on font cache cache overflow, for some reason. |
| if (zero_hfont != NULL) { |
| if (!DeleteObject(zero_hfont)) { |
| WIN32_DELETE_ERROR(); |
| } |
| } |
| #endif |
| // conservatively check if the bbox is to large |
| if ( |
| fabs(0.9 * |
| (abssin * zero_approx_width + abscos * zero_approx_height) * trans_ymult) > boundy || |
| fabs(0.9 * |
| (abscos * zero_approx_width + abssin * zero_approx_height) * trans_xmult) > boundx) { |
| |
| return; // Text bigger than user wants; don't draw |
| } |
| |
| int width, height; |
| font_ptr current_font = gl_state.font_info.get_font_info( |
| gl_state.currentfontsize, |
| gl_state.currentfontrotation |
| ); |
| #ifdef X11 |
| XGlyphInfo extents; |
| XftTextExtentsUtf8(x11_state.display, current_font, reinterpret_cast<const FcChar8*>(text), text_byte_length, &extents); |
| width = extents.width; |
| height = extents.height; |
| #else /* WC : WIN32 */ |
| if (win32_state.hGraphicsFont == NULL) { |
| // if the font isn't already created, create it. |
| // note: set to NULL by settextattrs(...) if something changed |
| win32_state.hGraphicsFont = CreateFontIndirect(current_font); |
| |
| if (!win32_state.hGraphicsFont) { |
| WIN32_CREATE_ERROR(); |
| } |
| // Select created font into specified device context |
| if (!SelectObject(win32_state.hGraphicsDC, win32_state.hGraphicsFont)) { |
| WIN32_SELECT_ERROR(); |
| } |
| } |
| |
| if (SetTextColor( |
| win32_state.hGraphicsDC, |
| convert_to_win_color(gl_state.foreground_color) |
| ) == CLR_INVALID) { |
| WIN32_DRAW_ERROR(); |
| } |
| |
| SIZE textsize; |
| |
| if (!GetTextExtentPoint32W( |
| win32_state.hGraphicsDC, |
| WIN32_wchar_text, |
| WIN32_wchar_text_len, |
| &textsize) |
| ) { |
| WIN32_DRAW_ERROR(); |
| } |
| |
| width = (int) (textsize.cx * abscos + textsize.cy*abssin); |
| height = (int) (textsize.cx * abssin + textsize.cy*abscos); |
| #endif |
| |
| // Text width and height in whatever coordinate system the user has active (SCREEN or WORLD) |
| float coord_width = width * trans_xmult; |
| float coord_height = height * trans_ymult; |
| |
| if ((fabs(coord_width) > fabs(boundx)) || (fabs(coord_height) > fabs(boundy))) { |
| return; // don't draw if it won't fit in xbound or ybound. |
| } |
| |
| // These are more-or-less magic offsets. The members of extents are not documented |
| // anywhere I could find, and after much experimentation, I determined some of their |
| // meaning, and derived offsets to find the true center of the text - ie the center |
| // point in the x direction, with the y center being halfway up a normal letter |
| // (eg. 'a', ie. nothing below the baseline (like 'p'), or anything extra tall (like 'b')). |
| // |
| // XGlyphInfo.{x,y} - relative location of the start of the text to the point passed |
| // to XftDrawString*(). |
| // XGlyphInfo.{x,y}Off - relative location of the end of the text to (x,y); where |
| // the drawing of the next character would start, I believe. |
| // XGlyphInfo.{width,height} - these ones mean what they say - ie. width and height |
| // of a bounding rectangle, but note that they will change based on rotation and |
| // whether or not the text contains non short letters like 'p' or 'b'). |
| // |
| // Also, it should be noted that with rotation, |
| // XftFont.{height,descent,ascent,max_advance_width} cannot be trusted. |
| // You would have to take those values from an unrotated font. |
| #ifdef X11 |
| // these two work almost perfectly, with aligned baselines, but only for the interval [0-90] |
| //float X11_xmagicoffset = (extents.width - (extents.x + extents.xOff))*trans_coord.stow_xmult; |
| // float X11_ymagicoffset = - (extents.y - extents.height)*trans_coord.stow_ymult; |
| |
| // however just leaving them at 0 works good enough for [0-360], however baselines |
| // will not be aligned, and text with different heights and the same yc will look bad next |
| // to each other. If you would like aligned baselines, use the previous equations, but note |
| // their caveats. |
| float X11_xmagicoffset = 0; |
| float X11_ymagicoffset = 0; |
| #endif |
| |
| t_bound_box text_bbox( |
| #ifdef X11 |
| t_point( |
| xc + (-coord_width + X11_xmagicoffset) / 2.0, |
| yc + (-coord_height + X11_ymagicoffset) / 2.0 |
| ), |
| #elif defined WIN32 |
| t_point(xc - coord_width / 2, yc - wrld_height / 2), |
| #endif |
| coord_width, |
| coord_height |
| ); |
| |
| if (rect_off_screen(text_bbox)) { |
| return; |
| } |
| |
| // #define SHOW_TEXT_BBOX // useful for debugging text placement. |
| #ifdef SHOW_TEXT_BBOX |
| drawrect(text_bbox); |
| t_color save = getcolor(); |
| setcolor(RED); |
| |
| if (fabs(coord_width) < fabs(boundx)) { |
| drawline(xc, yc - boundy / 2, xc, yc + boundy / 2); |
| } |
| if (fabs(coord_height) < fabs(boundy)) { |
| drawline(xc - boundx / 2, yc, xc + boundx / 2, yc); |
| } |
| |
| setcolor(GREEN); |
| drawline(xc, yc - coord_height / 2, xc, yc + coord_height / 2); |
| drawline(xc - coord_width / 2, yc, xc + coord_width / 2, yc); |
| setcolor(save); |
| #endif |
| |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef X11 |
| XftDrawStringUtf8( |
| x11_state.draw_area_draw, |
| &x11_state.xft_currentcolor, |
| current_font, |
| // more magic offsets |
| xworld_to_scrn(text_bbox.left()) + extents.x, |
| yworld_to_scrn(text_bbox.top()) + extents.y - extents.height, |
| reinterpret_cast<const FcChar8*>(text), |
| text_byte_length |
| ); |
| #elif defined WIN32 |
| float WIN_xtextoffset = 0; |
| float WIN_ytextoffset = 0; |
| float normalized_angle = (float) (angle - (int) (angle / (2 * PI))*2 * PI); |
| if (normalized_angle < PI / 2) { // quadrant I |
| WIN_ytextoffset = textsize.cx*abssin; |
| } else if (normalized_angle < PI) { // quadrant II |
| WIN_ytextoffset = (float) height; |
| WIN_xtextoffset = textsize.cx*abscos; |
| } else if (normalized_angle < PI * 3 / 2) { // quadrant III |
| WIN_xtextoffset = (float) width; |
| WIN_ytextoffset = textsize.cy*abscos; |
| } else { // quadrant IV |
| WIN_xtextoffset = textsize.cy*abssin; |
| } |
| SetBkMode(win32_state.hGraphicsDC, TRANSPARENT); |
| if (TextOutW( |
| win32_state.hGraphicsDC, |
| xworld_to_scrn(text_bbox.left()) + (int) WIN_xtextoffset, |
| yworld_to_scrn(text_bbox.bottom()) + (int) WIN_ytextoffset, |
| WIN32_wchar_text, |
| WIN32_wchar_text_len |
| ) == 0) { |
| WIN32_DRAW_ERROR(); |
| } |
| |
| delete[] WIN32_wchar_text; |
| #endif |
| } else { |
| fprintf(gl_state.ps, "gsave\n"); |
| fprintf(gl_state.ps, "%.2f %.2f moveto\n", x_to_post(xc), y_to_post(yc)); |
| fprintf(gl_state.ps, "0.8 0.8 scale\n"); // text comes out a little bit bigger in ps than X11 |
| fprintf(gl_state.ps, "%d rotate\n", static_cast<int>(gl_state.currentfontrotation) % 360); |
| fprintf(gl_state.ps, "(%s) 0 0 rcenshow\n", text); |
| fprintf(gl_state.ps, "grestore\n"); |
| } |
| } |
| |
| void |
| set_coordinate_system(t_coordinate_system coord) |
| { |
| gl_state.currentcoordinatesystem = coord; |
| } |
| |
| void |
| flushinput(void) { |
| if (gl_state.disp_type != SCREEN) |
| return; |
| #ifdef X11 |
| XFlush(x11_state.display); |
| #endif |
| } |
| |
| void set_visible_world(float left, float bottom, float right, float top) { |
| set_visible_world(t_bound_box(left, bottom, right, top)); |
| } |
| |
| void set_visible_world(const t_bound_box& bounds) { |
| /* Sets the coordinate system the user wants to draw into. */ |
| |
| trans_coord.xleft = bounds.left(); |
| trans_coord.xright = bounds.right(); |
| trans_coord.ytop = bounds.top(); |
| trans_coord.ybot = bounds.bottom(); |
| |
| /* Save initial world coordinates to allow full view button |
| * to zoom all the way out. |
| */ |
| trans_coord.init_xleft = trans_coord.xleft; |
| trans_coord.init_xright = trans_coord.xright; |
| trans_coord.init_ytop = trans_coord.ytop; |
| trans_coord.init_ybot = trans_coord.ybot; |
| |
| if (gl_state.disp_type == SCREEN) { |
| update_transform(); |
| } else { |
| update_ps_transform(); |
| } |
| } |
| |
| /* Draw the current message in the text area at the screen bottom. */ |
| void |
| draw_message(void) { |
| int savefontsize; |
| t_color savecolor; |
| float ylow; |
| |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef X11 |
| XClearWindow(x11_state.display, x11_state.textarea); |
| |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[WHITE])); |
| XDrawRectangle(x11_state.display, x11_state.textarea, x11_state.gc_menus, 0, 0, |
| trans_coord.top_width - MWIDTH, T_AREA_HEIGHT); |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK])); |
| XDrawLine(x11_state.display, x11_state.textarea, x11_state.gc_menus, 0, T_AREA_HEIGHT - 1, |
| trans_coord.top_width - MWIDTH, T_AREA_HEIGHT - 1); |
| XDrawLine(x11_state.display, x11_state.textarea, x11_state.gc_menus, |
| trans_coord.top_width - MWIDTH - 1, 0, trans_coord.top_width - MWIDTH - 1, |
| T_AREA_HEIGHT - 1); |
| |
| menutext( |
| x11_state.textarea_draw, |
| (trans_coord.top_width - MWIDTH) / 2, |
| T_AREA_HEIGHT / 2, |
| gl_state.statusMessage |
| ); |
| #else |
| if (!InvalidateRect(win32_state.hStatusWnd, NULL, TRUE)) |
| WIN32_DRAW_ERROR(); |
| if (!UpdateWindow(win32_state.hStatusWnd)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } else { |
| /* Draw the message in the bottom margin. Printer's generally can't * |
| * print on the bottom 1/4" (area with y < 18 in PostScript coords.) */ |
| |
| savecolor = gl_state.foreground_color; |
| setcolor(BLACK); |
| savefontsize = gl_state.currentfontsize; |
| setfontsize(MENU_FONT_SIZE - 2); /* Smaller OK on paper */ |
| ylow = trans_coord.ps_bot - 8; |
| fprintf(gl_state.ps, "(%s) %.2f %.2f censhow\n", gl_state.statusMessage, |
| (trans_coord.ps_left + trans_coord.ps_right) / 2., ylow); |
| setcolor(savecolor); |
| setfontsize(savefontsize); |
| } |
| } |
| |
| /* Changes the message to be displayed on screen. */ |
| void |
| update_message(const string& msg) { |
| strncpy(gl_state.statusMessage, msg.c_str(), BUFSIZE); |
| gl_state.statusMessage[BUFSIZE-1] = '\0'; //Ensure null terimination |
| draw_message(); |
| #ifdef X11 |
| // Make this appear immediately. Win32 does that automaticaly. |
| XFlush(x11_state.display); |
| #endif // X11 |
| } |
| |
| /* Zooms in menu button pressed. Zoom in at center |
| * of graphics area. |
| */ |
| static void |
| zoom_in(void (*drawscreen) (void)) { |
| float xcen, ycen; |
| |
| xcen = (trans_coord.xright + trans_coord.xleft) / 2; |
| ycen = (trans_coord.ybot + trans_coord.ytop) / 2; |
| |
| handle_zoom_in(xcen, ycen, drawscreen); |
| } |
| |
| /* Zoom out menu button pressed. Zoom out from center |
| * of graphics area. |
| */ |
| static void |
| zoom_out(void (*drawscreen) (void)) { |
| float xcen, ycen; |
| |
| xcen = (trans_coord.xright + trans_coord.xleft) / 2; |
| ycen = (trans_coord.ybot + trans_coord.ytop) / 2; |
| |
| handle_zoom_out(xcen, ycen, drawscreen); |
| } |
| |
| /* Zooms in by a factor of ZOOM_FACTOR */ |
| static void |
| handle_zoom_in(float x, float y, void (*drawscreen) (void)) { |
| //make xright - xleft = 0.6 of the original distance |
| trans_coord.xleft = x - (x - trans_coord.xleft) / ZOOM_FACTOR; |
| trans_coord.xright = x + (trans_coord.xright - x) / ZOOM_FACTOR; |
| //make ybot - ytop = 0.6 of the original distance |
| trans_coord.ytop = y - (y - trans_coord.ytop) / ZOOM_FACTOR; |
| trans_coord.ybot = y + (trans_coord.ybot - y) / ZOOM_FACTOR; |
| |
| update_transform(); |
| if (drawscreen != NULL) { |
| drawscreen(); |
| } |
| } |
| |
| /* Zooms out by a factor of ZOOM_FACTOR */ |
| static void |
| handle_zoom_out(float x, float y, void (*drawscreen) (void)) { |
| //restore the original distances before previous zoom in |
| trans_coord.xleft = x - (x - trans_coord.xleft)* ZOOM_FACTOR; |
| trans_coord.xright = x + (trans_coord.xright - x)* ZOOM_FACTOR; |
| trans_coord.ytop = y - (y - trans_coord.ytop)* ZOOM_FACTOR; |
| trans_coord.ybot = y + (trans_coord.ybot - y)* ZOOM_FACTOR; |
| |
| update_transform(); |
| if (drawscreen != NULL) { |
| drawscreen(); |
| } |
| } |
| |
| /* Sets the view back to the initial view set by set_visible_world (i.e. a full * |
| * view) of all the graphics. */ |
| static void |
| zoom_fit(void (*drawscreen) (void)) { |
| trans_coord.xleft = trans_coord.init_xleft; |
| trans_coord.xright = trans_coord.init_xright; |
| trans_coord.ytop = trans_coord.init_ytop; |
| trans_coord.ybot = trans_coord.init_ybot; |
| |
| update_transform(); |
| drawscreen(); |
| } |
| |
| /* Moves view 1/2 screen up. */ |
| static void |
| translate_up(void (*drawscreen) (void)) { |
| float ystep; |
| |
| ystep = (trans_coord.ybot - trans_coord.ytop) / 2; |
| trans_coord.ytop -= ystep; |
| trans_coord.ybot -= ystep; |
| update_transform(); |
| drawscreen(); |
| } |
| |
| /* Moves view 1/2 screen down. */ |
| static void |
| translate_down(void (*drawscreen) (void)) { |
| float ystep; |
| |
| ystep = (trans_coord.ybot - trans_coord.ytop) / 2; |
| trans_coord.ytop += ystep; |
| trans_coord.ybot += ystep; |
| update_transform(); |
| drawscreen(); |
| } |
| |
| /* Moves view 1/2 screen left. */ |
| static void |
| translate_left(void (*drawscreen) (void)) { |
| |
| float xstep; |
| |
| xstep = (trans_coord.xright - trans_coord.xleft) / 2; |
| trans_coord.xleft -= xstep; |
| trans_coord.xright -= xstep; |
| update_transform(); |
| drawscreen(); |
| } |
| |
| /* Moves view 1/2 screen right. */ |
| static void |
| translate_right(void (*drawscreen) (void)) { |
| float xstep; |
| |
| xstep = (trans_coord.xright - trans_coord.xleft) / 2; |
| trans_coord.xleft += xstep; |
| trans_coord.xright += xstep; |
| update_transform(); |
| drawscreen(); |
| } |
| |
| /* Panning is enabled by pressing and holding down mouse wheel |
| * (or middle mouse button) |
| */ |
| static void |
| panning_execute(int x, int y, void (*drawscreen) (void)) { |
| float x_change_world, y_change_world; |
| |
| x_change_world = xscrn_to_world(x) - xscrn_to_world(pan_state.previous_x); |
| y_change_world = yscrn_to_world(y) - yscrn_to_world(pan_state.previous_y); |
| trans_coord.xleft -= x_change_world; |
| trans_coord.xright -= x_change_world; |
| trans_coord.ybot -= y_change_world; |
| trans_coord.ytop -= y_change_world; |
| |
| update_transform(); |
| drawscreen(); |
| |
| pan_state.previous_x = x; |
| pan_state.previous_y = y; |
| } |
| |
| /* Turn panning_enabled on */ |
| static void |
| panning_on(int start_x, int start_y) { |
| pan_state.previous_x = start_x; |
| pan_state.previous_y = start_y; |
| pan_state.panning_enabled = true; |
| } |
| |
| /* Turn panning_enabled off */ |
| static void |
| panning_off(void) { |
| pan_state.panning_enabled = false; |
| } |
| |
| |
| // Updates the graphics transformation so that the graphics drawn within the |
| // box (in pixels) defined by x[0],y[0] to x[1],y[1] will be scaled to fill |
| // the whole window area. |
| |
| static void |
| update_win(int x[2], int y[2], void (*drawscreen)(void)) { |
| float x1, x2, y1, y2; |
| |
| x[0] = min(x[0], trans_coord.top_width - MWIDTH); /* Can't go under menu */ |
| x[1] = min(x[1], trans_coord.top_width - MWIDTH); |
| y[0] = min(y[0], trans_coord.top_height - T_AREA_HEIGHT); /* Can't go under text area */ |
| y[1] = min(y[1], trans_coord.top_height - T_AREA_HEIGHT); |
| |
| if ((x[0] == x[1]) || (y[0] == y[1])) { |
| printf("Illegal (zero area) window. Window unchanged.\n"); |
| return; |
| } |
| x1 = xscrn_to_world(min(x[0], x[1])); |
| x2 = xscrn_to_world(max(x[0], x[1])); |
| y1 = yscrn_to_world(min(y[0], y[1])); |
| y2 = yscrn_to_world(max(y[0], y[1])); |
| trans_coord.xleft = x1; |
| trans_coord.xright = x2; |
| trans_coord.ytop = y1; |
| trans_coord.ybot = y2; |
| update_transform(); |
| drawscreen(); |
| } |
| |
| /* The window button was pressed. Let the user click on the two * |
| * diagonally opposed corners, and zoom in on this area. */ |
| static void |
| adjustwin(void (*drawscreen) (void)) { |
| #ifdef X11 |
| |
| XEvent report; |
| int corner, xold, yold, x[2], y[2]; |
| |
| corner = 0; |
| xold = -1; |
| yold = -1; /* Don't need to init yold, but stops compiler warning. */ |
| |
| while (corner < 2) { |
| XNextEvent(x11_state.display, &report); |
| switch (report.type) { |
| case Expose: |
| x11_handle_expose(report, drawscreen); |
| if (report.xexpose.window == x11_state.toplevel) { |
| xold = -1; /* No rubber band on screen */ |
| } |
| break; |
| case ConfigureNotify: |
| x11_handle_configure_notify(report, drawscreen); |
| break; |
| case ButtonPress: |
| #ifdef VERBOSE |
| printf("Got a buttonpress.\n"); |
| printf("Window ID is: %ld.\n", report.xbutton.window); |
| printf("Location (%d, %d).\n", report.xbutton.x, |
| report.xbutton.y); |
| #endif |
| if (report.xbutton.window != x11_state.toplevel) break; |
| x[corner] = report.xbutton.x; |
| y[corner] = report.xbutton.y; |
| if (corner == 0) { |
| /* XSelectInput (x11_state.display, x11_state.toplevel, ExposureMask | |
| StructureNotifyMask | ButtonPressMask | PointerMotionMask); */ |
| } else { |
| update_win(x, y, drawscreen); |
| } |
| corner++; |
| break; |
| case MotionNotify: |
| if (corner) { |
| #ifdef VERBOSE |
| printf("Got a MotionNotify Event.\n"); |
| printf("x: %d y: %d\n", report.xmotion.x, report.xmotion.y); |
| #endif |
| if (xold >= 0) { /* xold set -ve before we draw first box */ |
| // Erase prior box. |
| set_draw_mode(DRAW_XOR); |
| XDrawRectangle(x11_state.display, x11_state.toplevel, x11_state.gc_xor, |
| min(x[0], xold), min(y[0], yold), abs(x[0] - xold), abs(y[0] - yold)); |
| set_draw_mode(DRAW_NORMAL); |
| } |
| /* Don't allow user to window under menu region */ |
| xold = min(report.xmotion.x, trans_coord.top_width - 1 - MWIDTH); |
| yold = report.xmotion.y; |
| set_draw_mode(DRAW_XOR); |
| |
| setlinewidth(1); |
| setlinestyle(DASHED); |
| setcolor(gl_state.background_color); |
| |
| // Draw rubber band box. |
| XDrawRectangle(x11_state.display, x11_state.toplevel, x11_state.gc_xor, min(x[0], xold), |
| min(y[0], yold), abs(x[0] - xold), abs(y[0] - yold)); |
| set_draw_mode(DRAW_NORMAL); |
| } |
| break; |
| |
| default: |
| break; // Other event type: ignore it. |
| } |
| } |
| /* XSelectInput (x11_state.display, x11_state.toplevel, ExposureMask | StructureNotifyMask |
| | ButtonPressMask); */ |
| #else /* Win32 */ |
| /* Implemented as WM_LB... events */ |
| |
| /* Begin window adjust */ |
| if (win32_state.windowAdjustFlag == WINDOW_DEACTIVATED) { |
| win32_state.windowAdjustFlag = WAITING_FOR_FIRST_CORNER_POINT; |
| } |
| #endif |
| } |
| |
| static void |
| postscript(void (*drawscreen) (void)) { |
| /* Takes a snapshot of the screen and stores it in pic?.ps. The * |
| * first picture goes in pic1.ps, the second in pic2.ps, etc. */ |
| |
| int piccount = 1; |
| int success; |
| char fname[BUFSIZE]; |
| |
| |
| /* |
| * Find a filename which does not exist |
| */ |
| while(true) { |
| sprintf(fname, "pic%d.ps", piccount); |
| |
| FILE* f = fopen(fname, "r"); |
| if(f) { |
| //File exists, try next number |
| fclose(f); |
| ++piccount; |
| } else { |
| //File doesn't exist, use this filename |
| break; |
| } |
| } |
| |
| printf("Writing postscript output to file %s\n", fname); |
| success = init_postscript(fname); |
| |
| if (success) { |
| drawscreen(); |
| close_postscript(); |
| } else { |
| printf("Error initializing for postscript output.\n"); |
| #ifdef WIN32 |
| MessageBoxW(win32_state.hMainWnd, L"Error initializing postscript output.", NULL, MB_OK); |
| #endif |
| } |
| } |
| |
| static void |
| proceed(void (*drawscreen) (void)) { |
| (void) drawscreen; // suppress unused warning |
| gl_state.ProceedPressed = true; |
| } |
| |
| static void |
| quit(void (*drawscreen) (void)) { |
| (void) drawscreen; // suppress unused warning |
| close_graphics(); |
| exit(0); |
| } |
| |
| /* Release all my drawing structures (through the X server) and * |
| * close down the connection. */ |
| void |
| close_graphics(void) { |
| if (!gl_state.initialized) |
| return; |
| |
| gl_state.font_info.clear(); |
| |
| for (int i = 0; i < button_state.num_buttons; ++i) { |
| unmap_button(i); |
| } |
| free(button_state.button); |
| button_state.button = NULL; |
| button_state.num_buttons = 0; |
| |
| #ifdef X11 |
| XFreeGC(x11_state.display, x11_state.gc_normal); |
| XFreeGC(x11_state.display, x11_state.gc_xor); |
| XFreeGC(x11_state.display, x11_state.gc_menus); |
| |
| XftDrawDestroy(x11_state.toplevel_draw); |
| XftDrawDestroy(x11_state.menu_draw); |
| XftDrawDestroy(x11_state.textarea_draw); |
| XftDrawDestroy(x11_state.draw_buffer_draw); |
| XFreePixmap(x11_state.display, x11_state.draw_buffer); |
| |
| x11_state.colormap_to_use = -1; // is free()'d by XCloseDisplay |
| memset(&x11_state.visual_info, 0, sizeof (x11_state.visual_info)); // dont need to free this |
| |
| XCloseDisplay(x11_state.display); |
| #elif defined WIN32 |
| // Destroy the window |
| if (!DestroyWindow(win32_state.hMainWnd)) |
| WIN32_DRAW_ERROR(); |
| |
| // free the window class (type information held by MS Windows) |
| // for each of the four window types we created. Otherwise a |
| // later call to init_graphics to open the graphics window up again |
| // will fail. |
| if (!UnregisterClassW(szAppName, GetModuleHandle(NULL))) |
| WIN32_DRAW_ERROR(); |
| if (!UnregisterClassW(szGraphicsName, GetModuleHandle(NULL))) |
| WIN32_DRAW_ERROR(); |
| if (!UnregisterClassW(szStatusName, GetModuleHandle(NULL))) |
| WIN32_DRAW_ERROR(); |
| if (!UnregisterClassW(szButtonsName, GetModuleHandle(NULL))) |
| WIN32_DRAW_ERROR(); |
| #endif |
| |
| // Destroy cairo things |
| cairo_destroy(x11_state.ctx); |
| cairo_surface_destroy(x11_state.cairo_surface); |
| x11_state.ctx = NULL; // Important to NULL these pointers in case init_cairo is called again |
| x11_state.cairo_surface = NULL; |
| |
| gl_state.initialized = false; |
| } |
| |
| /* Opens a file for PostScript output. The header information, * |
| * clipping path, etc. are all dumped out. If the file could * |
| * not be opened, the routine returns 0; otherwise it returns 1. */ |
| int init_postscript(const char *fname) { |
| gl_state.ps = fopen(fname, "w"); |
| if (gl_state.ps == NULL) { |
| printf("Error: could not open %s for PostScript output.\n", fname); |
| printf("Drawing to screen instead.\n"); |
| return (0); |
| } |
| gl_state.disp_type = POSTSCRIPT; /* Graphics go to postscript file now. */ |
| |
| /* Header for minimal conformance with the Adobe structuring convention */ |
| fprintf(gl_state.ps, "%%!PS-Adobe-1.0\n"); |
| fprintf(gl_state.ps, "%%%%DocumentFonts: Helvetica\n"); |
| fprintf(gl_state.ps, "%%%%Pages: 1\n"); |
| /* Set up postscript transformation macros and page boundaries */ |
| update_ps_transform(); |
| /* Bottom margin is at ps_bot - 15. to leave room for the on-screen message. */ |
| fprintf(gl_state.ps, "%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", |
| trans_coord.ps_left, trans_coord.ps_bot - 15., |
| trans_coord.ps_right, trans_coord.ps_top); |
| fprintf(gl_state.ps, "%%%%EndComments\n"); |
| |
| fprintf(gl_state.ps, "/censhow %%draw a centered string\n"); |
| fprintf(gl_state.ps, " { moveto %% move to proper spot\n"); |
| fprintf(gl_state.ps, " dup stringwidth pop %% get x length of string\n"); |
| fprintf(gl_state.ps, " -2 div %% Proper left start\n"); |
| fprintf(gl_state.ps, " yoff rmoveto %% Move left that much and down half font height\n"); |
| fprintf(gl_state.ps, " show newpath } def %% show the string\n\n"); |
| |
| fprintf(gl_state.ps, "/rcenshow %%draw a centered string\n" |
| " { rmoveto %% move to proper spot\n" |
| " dup stringwidth pop %% get x length of string\n" |
| " -2 div %% Proper left start\n" |
| " yoff rmoveto %% Move left that much and down half font height\n" |
| " show newpath } def %% show the string\n"); |
| |
| fprintf(gl_state.ps, "/setfontsize %% set font to desired size and compute " |
| "centering yoff\n"); |
| fprintf(gl_state.ps, " { /Helvetica findfont\n"); |
| fprintf(gl_state.ps, " exch scalefont\n"); |
| fprintf(gl_state.ps, " setfont %% Font size set ...\n\n"); |
| fprintf(gl_state.ps, " 0 0 moveto %% Get vertical centering offset\n"); |
| fprintf(gl_state.ps, " (Xg) true charpath\n"); |
| fprintf(gl_state.ps, " flattenpath pathbbox\n"); |
| fprintf(gl_state.ps, " /ascent exch def pop -1 mul /descent exch def pop\n"); |
| fprintf(gl_state.ps, " newpath\n"); |
| fprintf(gl_state.ps, " descent ascent sub 2 div /yoff exch def } def\n\n"); |
| |
| fprintf(gl_state.ps, "%% Next two lines for debugging only.\n"); |
| fprintf(gl_state.ps, "/str 20 string def\n"); |
| fprintf(gl_state.ps, "/pnum {str cvs print ( ) print} def\n"); |
| |
| fprintf(gl_state.ps, "/drawline %% draw a line from (x2,y2) to (x1,y1)\n"); |
| fprintf(gl_state.ps, " { moveto lineto stroke } def\n\n"); |
| |
| fprintf(gl_state.ps, "/rect %% outline a rectangle \n"); |
| fprintf(gl_state.ps, " { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"); |
| fprintf(gl_state.ps, " x1 y1 moveto\n"); |
| fprintf(gl_state.ps, " x2 y1 lineto\n"); |
| fprintf(gl_state.ps, " x2 y2 lineto\n"); |
| fprintf(gl_state.ps, " x1 y2 lineto\n"); |
| fprintf(gl_state.ps, " closepath } def\n\n"); |
| |
| fprintf(gl_state.ps, "/drawrect %% draw outline of a rectanagle\n"); |
| fprintf(gl_state.ps, " { rect stroke } def\n\n"); |
| |
| fprintf(gl_state.ps, "/fillrect %% fill in a rectanagle\n"); |
| fprintf(gl_state.ps, " { rect fill } def\n\n"); |
| |
| fprintf(gl_state.ps, "/drawarc { arc stroke } def %% draw an arc\n"); |
| fprintf(gl_state.ps, "/drawarcn { arcn stroke } def " |
| " %% draw an arc in the opposite direction\n\n"); |
| |
| fprintf(gl_state.ps, "%%Fill a counterclockwise or clockwise arc sector, " |
| "respectively.\n"); |
| fprintf(gl_state.ps, "/fillarc { moveto currentpoint 5 2 roll arc closepath fill } " |
| "def\n"); |
| fprintf(gl_state.ps, "/fillarcn { moveto currentpoint 5 2 roll arcn closepath fill } " |
| "def\n\n"); |
| |
| fprintf(gl_state.ps, "/fillpoly { 3 1 roll moveto %% move to first point\n" |
| " 2 exch 1 exch {pop lineto} for %% line to all other points\n" |
| " closepath fill } def\n\n"); |
| |
| fprintf(gl_state.ps, "%%Color Definitions:\n"); |
| fprintf(gl_state.ps, "/white { 1 setgray } def\n"); |
| fprintf(gl_state.ps, "/black { 0 setgray } def\n"); |
| fprintf(gl_state.ps, "/grey55 { .55 setgray } def\n"); |
| fprintf(gl_state.ps, "/grey75 { .75 setgray } def\n"); |
| fprintf(gl_state.ps, "/red { 1 0 0 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/orange { 1 0.65 0 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/yellow { 1 1 0 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/green { 0 1 0 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/cyan { 0 1 1 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/blue { 0 0 1 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/purple { 0.63 0.13 0.94 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/pink { 1 0.75 0.8 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/lightpink { 1 0.71 0.76 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/darkgreen { 0 0.5 0 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/magenta { 1 0 1 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/bisque { 1 0.89 0.77 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/lightskyblue { 0.53 0.81 0.98 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/thistle { 0.85 0.75 0.85 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/plum { 0.87 0.63 0.87 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/khaki { 0.94 0.9 0.55 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/coral { 1 0.5 0.31 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/turquoise { 0.25 0.88 0.82 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/mediumpurple { 0.58 0.44 0.86 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/darkslateblue { 0.28 0.24 0.55 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/darkkhaki { 0.74 0.72 0.42 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/lightmediumblue { 0.33 0.33 1.00 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/saddlebrown { 0.55 0.27 0.07 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/firebrick { 0.70 0.13 0.13 setrgbcolor } def\n"); |
| fprintf(gl_state.ps, "/limegreen { 0.20 0.80 0.20 setrgbcolor } def\n"); |
| |
| fprintf(gl_state.ps, "\n%%Solid and dashed line definitions:\n"); |
| fprintf(gl_state.ps, "/linesolid {[] 0 setdash} def\n"); |
| fprintf(gl_state.ps, "/linedashed {[3 3] 0 setdash} def\n"); |
| |
| fprintf(gl_state.ps, "\n%%%%EndProlog\n"); |
| fprintf(gl_state.ps, "%%%%Page: 1 1\n\n"); |
| |
| /* Set up PostScript graphics state to match current one. */ |
| force_setcolor(gl_state.foreground_color); |
| force_setlinestyle(gl_state.currentlinestyle); |
| force_setlinewidth(gl_state.currentlinewidth); |
| force_settextattrs(gl_state.currentfontsize, gl_state.currentfontrotation); |
| |
| /* Draw this in the bottom margin -- must do before the clippath is set */ |
| draw_message(); |
| |
| /* Set clipping on page. */ |
| fprintf(gl_state.ps, "%.2f %.2f %.2f %.2f rect ", trans_coord.ps_left, trans_coord.ps_bot, |
| trans_coord.ps_right, trans_coord.ps_top); |
| fprintf(gl_state.ps, "clip newpath\n\n"); |
| |
| return (1); |
| } |
| |
| /* Properly ends postscript output and redirects output to screen. */ |
| void close_postscript(void) { |
| |
| fprintf(gl_state.ps, "showpage\n"); |
| fprintf(gl_state.ps, "\n%%%%Trailer\n"); |
| fclose(gl_state.ps); |
| gl_state.disp_type = SCREEN; |
| update_transform(); /* Ensure screen world reflects any changes * |
| * made while printing. */ |
| |
| /* Need to make sure that we really set up the graphics context. |
| * The current font set indicates the last font used in a postscript call, |
| * etc., *NOT* the font set in the X11 or Win32 graphics context. Force the |
| * current font, colour etc. to be applied to the graphics context, so |
| * subsequent drawing commands work properly. |
| */ |
| |
| force_setcolor(gl_state.foreground_color); |
| force_setlinestyle(gl_state.currentlinestyle); |
| force_setlinewidth(gl_state.currentlinewidth); |
| force_settextattrs(gl_state.currentfontsize, gl_state.currentfontrotation); |
| } |
| |
| /* Sets up the default menu buttons on the right hand side of the window. */ |
| static void |
| build_default_menu(void) { |
| int i, xcen, x1, y1, bwid, bheight, space; |
| const int NUM_ARROW_BUTTONS = 4, NUM_STANDARD_BUTTONS = 12, SEPARATOR_BUTTON_INDEX = 8; |
| |
| #ifdef X11 |
| unsigned long valuemask; |
| XSetWindowAttributes menu_attributes; |
| |
| x11_state.menu = XCreateSimpleWindow( |
| x11_state.display, x11_state.toplevel, |
| trans_coord.top_width - MWIDTH, 0, MWIDTH, |
| trans_coord.display_height, 0, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK]), |
| x11_convert_to_xcolor(t_color::predef_colors[LIGHTGREY]) |
| ); |
| |
| x11_state.menu_draw = XftDrawCreate( |
| x11_state.display, |
| x11_state.menu, |
| x11_state.visual_info.visual, |
| x11_state.colormap_to_use |
| ); |
| menu_attributes.event_mask = ExposureMask; |
| // menu_attributes.event_mask = 0; |
| /* Ignore button presses on the menu background. */ |
| menu_attributes.do_not_propagate_mask = ButtonPressMask; |
| /* Keep menu on top right */ |
| menu_attributes.win_gravity = NorthEastGravity; |
| valuemask = CWWinGravity | CWEventMask | CWDontPropagate; |
| XChangeWindowAttributes(x11_state.display, x11_state.menu, valuemask, &menu_attributes); |
| XMapWindow(x11_state.display, x11_state.menu); |
| #endif |
| |
| button_state.button = static_cast<t_button *>(my_malloc(NUM_STANDARD_BUTTONS * sizeof (t_button))); |
| |
| /* Now do the arrow buttons */ |
| bwid = 28; |
| space = 3; |
| y1 = 10; |
| xcen = 51; |
| x1 = xcen - bwid / 2; |
| button_state.button[0].xleft = x1; |
| button_state.button[0].ytop = y1; |
| #ifdef X11 |
| setpoly(0, bwid / 2, bwid / 2, bwid / 3, -PI / 2.); /* Up */ |
| #else |
| button_state.button[0].type = BUTTON_TEXT; |
| #endif |
| strcpy(button_state.button[0].text, "U"); |
| button_state.button[0].fcn = translate_up; |
| |
| y1 += bwid + space; |
| x1 = xcen - 3 * bwid / 2 - space; |
| button_state.button[1].xleft = x1; |
| button_state.button[1].ytop = y1; |
| #ifdef X11 |
| setpoly(1, bwid / 2, bwid / 2, bwid / 3, PI); /* Left */ |
| #else |
| button_state.button[1].type = BUTTON_TEXT; |
| #endif |
| strcpy(button_state.button[1].text, "L"); |
| button_state.button[1].fcn = translate_left; |
| |
| x1 = xcen + bwid / 2 + space; |
| button_state.button[2].xleft = x1; |
| button_state.button[2].ytop = y1; |
| #ifdef X11 |
| setpoly(2, bwid / 2, bwid / 2, bwid / 3, 0); /* Right */ |
| #else |
| button_state.button[2].type = BUTTON_TEXT; |
| #endif |
| strcpy(button_state.button[2].text, "R"); |
| button_state.button[2].fcn = translate_right; |
| |
| y1 += bwid + space; |
| x1 = xcen - bwid / 2; |
| button_state.button[3].xleft = x1; |
| button_state.button[3].ytop = y1; |
| #ifdef X11 |
| setpoly(3, bwid / 2, bwid / 2, bwid / 3, +PI / 2.); /* Down */ |
| #else |
| button_state.button[3].type = BUTTON_TEXT; |
| #endif |
| strcpy(button_state.button[3].text, "D"); |
| button_state.button[3].fcn = translate_down; |
| |
| for (i = 0; i < NUM_ARROW_BUTTONS; i++) { |
| button_state.button[i].width = bwid; |
| button_state.button[i].height = bwid; |
| button_state.button[i].enabled = 1; |
| } |
| |
| /* Rectangular buttons */ |
| |
| y1 += bwid + space + 6; |
| space = 8; |
| bwid = 90; |
| bheight = 26; |
| x1 = xcen - bwid / 2; |
| for (i = NUM_ARROW_BUTTONS; i < NUM_STANDARD_BUTTONS; i++) { |
| button_state.button[i].xleft = x1; |
| button_state.button[i].ytop = y1; |
| button_state.button[i].type = BUTTON_TEXT; |
| button_state.button[i].width = bwid; |
| button_state.button[i].enabled = 1; |
| if (i != SEPARATOR_BUTTON_INDEX) { |
| button_state.button[i].height = bheight; |
| y1 += bheight + space; |
| } else { |
| button_state.button[i].height = 2; |
| button_state.button[i].type = BUTTON_SEPARATOR; |
| y1 += 2 + space; |
| } |
| } |
| |
| strcpy(button_state.button[4].text, "Zoom In"); |
| strcpy(button_state.button[5].text, "Zoom Out"); |
| strcpy(button_state.button[6].text, "Zoom Fit"); |
| strcpy(button_state.button[7].text, "Window"); |
| strcpy(button_state.button[8].text, "---1"); |
| strcpy(button_state.button[9].text, "PostScript"); |
| strcpy(button_state.button[10].text, "Proceed"); |
| strcpy(button_state.button[11].text, "Exit"); |
| |
| button_state.button[4].fcn = zoom_in; |
| button_state.button[5].fcn = zoom_out; |
| button_state.button[6].fcn = zoom_fit; |
| button_state.button[7].fcn = adjustwin; // see 'adjustButton' below in WIN32 section |
| button_state.button[8].fcn = NULL; |
| button_state.button[9].fcn = postscript; |
| button_state.button[10].fcn = proceed; |
| button_state.button[11].fcn = quit; |
| |
| for (i = 0; i < NUM_STANDARD_BUTTONS; i++) |
| map_button(i); |
| button_state.num_buttons = NUM_STANDARD_BUTTONS; |
| |
| #ifdef WIN32 |
| win32_state.adjustButton = 7; |
| if (!InvalidateRect(win32_state.hButtonsWnd, NULL, TRUE)) |
| WIN32_DRAW_ERROR(); |
| if (!UpdateWindow(win32_state.hButtonsWnd)) |
| WIN32_DRAW_ERROR(); |
| #endif |
| } |
| |
| /* Return information useful for debugging. |
| * Used to return the top-level window object too, but that made graphics.h |
| * export all windows and X11 headers to the client program, so VB deleted |
| * that object (mainwnd) from this structure. |
| */ |
| void get_report_structure(t_report *report) { |
| report->xmult = trans_coord.wtos_xmult; |
| report->ymult = trans_coord.wtos_ymult; |
| report->xleft = trans_coord.xleft; |
| report->xright = trans_coord.xright; |
| report->ytop = trans_coord.ytop; |
| report->ybot = trans_coord.ybot; |
| report->ps_xmult = trans_coord.ps_xmult; |
| report->ps_ymult = trans_coord.ps_ymult; |
| report->top_width = trans_coord.top_width; |
| report->top_height = trans_coord.top_height; |
| } |
| |
| void set_mouse_move_input(bool enable) { |
| gl_state.get_mouse_move_input = enable; |
| } |
| |
| void set_keypress_input(bool enable) { |
| gl_state.get_keypress_input = enable; |
| } |
| |
| void enable_or_disable_button(int ibutton, bool enabled) { |
| |
| if (button_state.button[ibutton].type != BUTTON_SEPARATOR) { |
| button_state.button[ibutton].enabled = enabled; |
| #ifdef WIN32 |
| EnableWindow(button_state.button[ibutton].hwnd, button_state.button[ibutton].enabled); |
| #else // X11 |
| x11_drawbut(ibutton); |
| XFlush(x11_state.display); |
| #endif |
| } |
| } |
| |
| void set_draw_mode(enum e_draw_mode draw_mode) { |
| /* Set normal (overwrite) or xor (useful for rubber-banding) |
| * drawing mode. |
| */ |
| |
| if (draw_mode == DRAW_NORMAL) { |
| #ifdef X11 |
| x11_state.current_gc = x11_state.gc_normal; |
| #else |
| if (!SetROP2(win32_state.hGraphicsDC, R2_COPYPEN)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| } else { // DRAW_XOR |
| #ifdef X11 |
| x11_state.current_gc = x11_state.gc_xor; |
| #else |
| if (!SetROP2(win32_state.hGraphicsDC, R2_XORPEN)) |
| WIN32_SELECT_ERROR(); |
| #endif |
| } |
| // We've changed which graphics context is active. Make sure the |
| // current graphics drawing state is applied to the active context. |
| force_setcolor(gl_state.foreground_color); |
| force_setlinestyle(gl_state.currentlinestyle, gl_state.currentlinecap); |
| force_setlinewidth(gl_state.currentlinewidth); |
| gl_state.current_draw_mode = draw_mode; |
| } |
| |
| void change_button_text(const char *button_name, const char *new_button_text) { |
| /* Change the text on a button with button_name to new_button_text. |
| * Not a strictly necessary function, since you could intead just |
| * destroy button_name and make a new buton. |
| */ |
| int i, bnum; |
| |
| bnum = -1; |
| |
| for (i = 4; i < button_state.num_buttons; i++) { |
| if (button_state.button[i].type == BUTTON_TEXT && |
| strcmp(button_state.button[i].text, button_name) == 0) { |
| bnum = i; |
| break; |
| } |
| } |
| |
| if (bnum != -1) { |
| strncpy(button_state.button[i].text, new_button_text, BUTTON_TEXT_LEN); |
| button_state.button[i].text[BUTTON_TEXT_LEN-1] = '\0'; //Ensure null terimination |
| #ifdef X11 |
| x11_drawbut(i); |
| #else // Win32 |
| wchar_t* WIN32_wchar_button_text = new wchar_t[BUTTON_TEXT_LEN]; |
| MultiByteToWideChar(CP_UTF8, 0, new_button_text, -1, |
| WIN32_wchar_button_text, BUTTON_TEXT_LEN); |
| |
| SetWindowTextW(button_state.button[bnum].hwnd, WIN32_wchar_button_text); |
| |
| delete[] WIN32_wchar_button_text; |
| #endif |
| } |
| } |
| |
| |
| /*********************************************** |
| * begin offscreen buffer function definitions * |
| ***********************************************/ |
| |
| void set_drawing_buffer(t_draw_to draw_mode) { |
| #ifdef X11 |
| if (draw_mode == ON_SCREEN) { |
| x11_state.draw_area = &(x11_state.toplevel); |
| x11_state.draw_area_draw = x11_state.toplevel_draw; |
| init_cairo(); |
| } |
| else if (draw_mode == OFF_SCREEN) { |
| x11_state.draw_area = &(x11_state.draw_buffer); |
| x11_state.draw_area_draw = x11_state.draw_buffer_draw; |
| init_cairo(); |
| } |
| else { |
| cerr << "New draw mode not yet supported in set_drawing_buffer" << endl; |
| } |
| gl_state.current_draw_to = draw_mode; |
| #endif /* X11 */ |
| } |
| |
| void copy_off_screen_buffer_to_screen() { |
| #ifdef X11 |
| if (x11_state.draw_area != &x11_state.draw_buffer) return; |
| |
| XCopyArea(x11_state.display, x11_state.draw_buffer, x11_state.toplevel, x11_state.current_gc, |
| 0, 0, x11_state.attributes.width, x11_state.attributes.height, 0, 0); |
| |
| cairo_xlib_surface_set_size( |
| x11_state.cairo_surface, |
| x11_state.attributes.width, |
| x11_state.attributes.height); |
| |
| XFlush(x11_state.display); |
| #endif /* X11 */ |
| } |
| |
| /************************************************* |
| * begin loading and drawing from file functions * |
| *************************************************/ |
| |
| Surface load_png_from_file(const char* file_path) { |
| return Surface(file_path); |
| } |
| |
| void draw_surface(const Surface& surface, float x, float y) { |
| cairo_surface_t* cairo_surface = surface.impl_->getSurface(); |
| if (cairo_surface != NULL) { |
| cairo_set_source_surface(x11_state.ctx, cairo_surface, |
| xworld_to_scrn(x), yworld_to_scrn(y)); |
| cairo_paint(x11_state.ctx); |
| } |
| else { |
| cerr << "Surface was not initialized" << endl; |
| } |
| } |
| |
| void draw_surface(const Surface& surface, t_point upper_left) { |
| draw_surface (surface, upper_left.x, upper_left.y); |
| } |
| |
| /*********************************************** |
| * begin cairo functions * |
| ***********************************************/ |
| |
| static void init_cairo() { |
| #ifdef X11 |
| // Destory old cairo things |
| cairo_destroy(x11_state.ctx); |
| cairo_surface_destroy(x11_state.cairo_surface); |
| |
| // Create new cairo things and set attributes |
| x11_state.cairo_surface = cairo_xlib_surface_create( |
| x11_state.display, |
| *x11_state.draw_area, |
| x11_state.visual_info.visual, |
| x11_state.attributes.width, x11_state.attributes.height); |
| cairo_xlib_surface_set_size(x11_state.cairo_surface, x11_state.attributes.width, x11_state.attributes.height); |
| x11_state.ctx = cairo_create(x11_state.cairo_surface); |
| cairo_set_antialias(x11_state.ctx, CAIRO_ANTIALIAS_NONE); // Turn off anti-aliasing |
| #endif // X11 |
| } |
| |
| |
| /* |
| * Functions that are helpful for automarking below. Call before the student |
| * could possibly call event_loop |
| */ |
| |
| void set_disable_event_loop (bool new_setting) { |
| gl_state.disable_event_loop = new_setting; |
| } |
| |
| |
| void set_redirect_to_postscript (bool new_setting) { |
| gl_state.redirect_to_postscript = new_setting; |
| } |
| |
| |
| /********************************** |
| * X-Windows Specific Definitions * |
| *********************************/ |
| #ifdef X11 |
| |
| /* Helper function called by init_graphics(). Not visible to client program. */ |
| static void x11_init_graphics(const char *window_name) { |
| char *display_name = NULL; |
| int x, y; /* window position */ |
| unsigned int border_width = 2; /* ignored by OpenWindows */ |
| XTextProperty windowName; |
| |
| unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */ |
| XGCValues values; |
| XEvent event; |
| |
| /* connect to X server */ |
| if ((x11_state.display = XOpenDisplay(display_name)) == NULL) { |
| fprintf(stderr, "Cannot connect to X server %s\n", |
| XDisplayName(display_name)); |
| exit(-1); |
| } |
| |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wold-style-cast" // Old style casts in these macros. |
| /* get screen size from display structure macro */ |
| x11_state.screen_num = DefaultScreen(x11_state.display); |
| trans_coord.display_width = DisplayWidth(x11_state.display, x11_state.screen_num); |
| trans_coord.display_height = DisplayHeight(x11_state.display, x11_state.screen_num); |
| #pragma GCC diagnostic pop |
| |
| x = 0; |
| y = 0; |
| |
| // Initial window will take 80% of the screen width & height (change if desired) |
| trans_coord.top_width = 4 * trans_coord.display_width / 5; |
| trans_coord.top_height = 4 * trans_coord.display_height / 5; |
| |
| // select a 24 bit TrueColor visual. Note that setting this visual |
| // for the top level window will make all children inherit it. |
| if (XMatchVisualInfo( |
| x11_state.display, |
| x11_state.screen_num, |
| 24, TrueColor, |
| &x11_state.visual_info) == 0) { |
| fprintf(stderr, "Warning failed to find 24-bit TrueColor visual\n"); |
| fprintf(stderr, " Graphics may not draw correctly\n"); |
| |
| } |
| |
| if(x11_state.visual_info.bits_per_rgb != 8) { |
| fprintf(stderr, "Warning found strange 24-bit TrueColor visual with %d-bit channels (expected 8)\n", x11_state.visual_info.bits_per_rgb); |
| fprintf(stderr, " Searching for any TrueColor visual with 8-bit channels\n"); |
| |
| //Try manually searching for a TrueColor mode with 8-bit pixels |
| XVisualInfo visual_template; |
| visual_template.c_class = TrueColor; //In c++ the XVisualInfo.class member is renamed c_class to avoid a name conflict with the class keyword |
| visual_template.bits_per_rgb = 8; |
| int num_matches = 0; |
| |
| XVisualInfo* matching_visual_info = XGetVisualInfo(x11_state.display, |
| VisualClassMask | VisualBitsPerRGBMask, |
| &visual_template, |
| &num_matches); |
| if(matching_visual_info == nullptr || num_matches == 0) { |
| fprintf(stderr, " Warning failed to find any TrueColor visual with 8-bits per channel\n"); |
| fprintf(stderr, " Graphics may not draw correctly\n"); |
| } else { |
| x11_state.visual_info = matching_visual_info[0]; |
| fprintf(stderr, " Found %d-bit TrueColor visual with 8-bits per channel\n", x11_state.visual_info.depth); |
| } |
| |
| XFree(matching_visual_info); |
| } |
| |
| if (x11_state.visual_info.bits_per_rgb != 8) { |
| fprintf(stderr, "Warning found strange 24-bit TrueColor visual: \n"); |
| fprintf(stderr, |
| " bit per color = %d, rmask = %lX, gmask = %lX, bmask = %lX\n", |
| x11_state.visual_info.bits_per_rgb, |
| x11_state.visual_info.red_mask, |
| x11_state.visual_info.green_mask, |
| x11_state.visual_info.blue_mask |
| ); |
| fprintf(stderr, " Graphics may not draw correctly\n"); |
| |
| } |
| |
| Window root_window = XDefaultRootWindow(x11_state.display); |
| |
| x11_state.colormap_to_use = XCreateColormap( |
| x11_state.display, |
| root_window, |
| x11_state.visual_info.visual, |
| AllocNone |
| ); |
| |
| XSetWindowAttributes attrs; |
| attrs.colormap = x11_state.colormap_to_use; |
| attrs.border_pixel = x11_convert_to_xcolor(t_color::predef_colors[BLACK]); |
| attrs.background_pixel = x11_convert_to_xcolor(gl_state.background_color); |
| |
| x11_state.toplevel = XCreateWindow( |
| x11_state.display, |
| root_window, |
| x, y, |
| trans_coord.top_width, trans_coord.top_height, |
| border_width, |
| x11_state.visual_info.depth, |
| InputOutput, |
| x11_state.visual_info.visual, |
| CWBackPixel | CWColormap | CWBorderPixel, |
| &attrs |
| ); |
| |
| x11_state.toplevel_draw = XftDrawCreate( |
| x11_state.display, |
| x11_state.toplevel, |
| x11_state.visual_info.visual, |
| x11_state.colormap_to_use |
| ); |
| |
| XRenderColor xr_textcolor; |
| xr_textcolor.red = 0x0; |
| xr_textcolor.green = 0x0; |
| xr_textcolor.blue = 0x0; |
| xr_textcolor.alpha = 0xffff; |
| |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wold-style-cast" // Old style casts in these macros. |
| XftColorAllocValue( |
| x11_state.display, |
| DefaultVisual(x11_state.display, x11_state.screen_num), |
| DefaultColormap(x11_state.display, x11_state.screen_num), |
| &xr_textcolor, |
| &x11_state.xft_menutextcolor |
| ); |
| #pragma GCC diagnostic pop |
| |
| XSelectInput(x11_state.display, x11_state.toplevel, ExposureMask | StructureNotifyMask | |
| ButtonPressMask | ButtonReleaseMask | PointerMotionMask | |
| KeyPressMask); |
| |
| /* Create default Graphics Contexts. valuemask = 0 -> use defaults. */ |
| x11_state.current_gc = x11_state.gc_normal = XCreateGC(x11_state.display, x11_state.toplevel, |
| valuemask, &values); |
| x11_state.gc_menus = XCreateGC(x11_state.display, x11_state.toplevel, valuemask, &values); |
| |
| /* Create XOR graphics context for Rubber Banding */ |
| values.function = GXxor; |
| values.foreground = x11_convert_to_xcolor(gl_state.background_color); |
| x11_state.gc_xor = XCreateGC(x11_state.display, x11_state.toplevel, (GCFunction | GCForeground), |
| &values); |
| |
| /* Initialize draw_buffer for XDraw calls and XftDraw calls */ |
| // get window attributes |
| XGetWindowAttributes(x11_state.display, x11_state.toplevel, &(x11_state.attributes)); |
| x11_state.draw_buffer = XCreatePixmap(x11_state.display, x11_state.toplevel, |
| x11_state.attributes.width, x11_state.attributes.height, x11_state.attributes.depth); |
| x11_state.draw_buffer_draw = XftDrawCreate( |
| x11_state.display, |
| x11_state.draw_buffer, |
| x11_state.visual_info.visual, |
| x11_state.colormap_to_use |
| ); |
| |
| // initialize draw_area to screen |
| set_drawing_buffer(ON_SCREEN); |
| |
| /* Set drawing defaults for user-drawable area. Use whatever the * |
| * initial values of the current stuff was set to. */ |
| force_settextattrs(gl_state.currentfontsize, gl_state.currentfontrotation); |
| force_setcolor(gl_state.foreground_color); |
| force_setlinestyle(gl_state.currentlinestyle); |
| force_setlinewidth(gl_state.currentlinewidth); |
| |
| // Need a non-const name to pass to XStringListTo... |
| // (even though X11 won't change it). |
| char *window_name_copy = static_cast<char *>(my_malloc(BUFSIZE * sizeof (char))); |
| strncpy(window_name_copy, window_name, BUFSIZE); |
| XStringListToTextProperty(&window_name_copy, 1, &windowName); |
| free(window_name_copy); |
| window_name_copy = NULL; |
| |
| XSetWMName(x11_state.display, x11_state.toplevel, &windowName); |
| /* XSetWMIconName (x11_state.display, x11_state.toplevel, &windowName); */ |
| |
| /* XStringListToTextProperty copies the window_name string into * |
| * windowName.value. Free this memory now. */ |
| |
| free(windowName.value); |
| |
| XMapWindow(x11_state.display, x11_state.toplevel); |
| x11_build_textarea(); |
| build_default_menu(); |
| |
| /* The following is completely unnecessary if the user is using the * |
| * interactive (event_loop) graphics. It waits for the first Expose * |
| * event before returning so that I can tell the window manager has got * |
| * the top-level window up and running. Thus the user can start drawing * |
| * into this window immediately, and there's no danger of the window not * |
| * being ready and output being lost. */ |
| XPeekIfEvent(x11_state.display, &event, x11_test_if_exposed, NULL); |
| force_settextattrs(gl_state.currentfontsize, gl_state.currentfontrotation); |
| } |
| |
| /* Helper function called by event_loop(). Not visible to client program. */ |
| static void |
| x11_event_loop(void (*act_on_mousebutton)(float x, float y, t_event_buttonPressed button_info), |
| void (*act_on_mousemove)(float x, float y), |
| void (*act_on_keypress)(char key_pressed, int keysym), |
| void (*drawscreen) (void)) { |
| XEvent report; |
| unsigned int last_skipped_button_press_button = -1; |
| int bnum; |
| float x, y; |
| |
| Atom wmDeleteMessage = XInternAtom(x11_state.display, "WM_DELETE_WINDOW", false); |
| XSetWMProtocols(x11_state.display, x11_state.toplevel, &wmDeleteMessage, 1); |
| |
| #define OFF 1 |
| #define ON 0 |
| |
| x11_turn_on_off(ON); |
| while (true) { |
| |
| XSync(x11_state.display, false); |
| XNextEvent(x11_state.display, &report); |
| #ifdef VERBOSE |
| cout << "Got an event of type: " << report.type << endl; |
| #endif |
| |
| if (x11_drop_redundant_panning (report, last_skipped_button_press_button)) |
| continue; // Drop this event |
| |
| switch (report.type) { |
| case Expose: |
| #ifdef VERBOSE |
| cout << "Got an Expose event with xexpose.count = " << report.xexpose.count << endl; |
| #endif |
| x11_handle_expose(report, drawscreen); |
| break; |
| case ConfigureNotify: |
| #ifdef VERBOSE |
| cout << "Got a ConfigureNotify event\n"; |
| #endif |
| x11_handle_configure_notify(report, drawscreen); |
| break; |
| case ButtonPress: |
| #ifdef VERBOSE |
| printf("Got a ButtonPress.\n"); |
| printf("Window ID is: %ld.\n", report.xbutton.window); |
| printf("Button pressed is: %d.\n(left click is 1; right click is 3; " |
| "scroll wheel click is 2; scroll wheel forward rotate is 4; " |
| "scroll wheel backward is 5.)\n", report.xbutton.button); |
| printf("Mask is: %d.\n", report.xbutton.state); |
| #endif |
| if (report.xbutton.window == x11_state.toplevel) { |
| x = xscrn_to_world(report.xbutton.x); |
| y = yscrn_to_world(report.xbutton.y); |
| |
| t_event_buttonPressed button_info; |
| /* t_event_buttonPressed is used as a structure for storing information about a * |
| * button press event. This information can be passed back to and used by a client* |
| * program. */ |
| x11_handle_button_info(&button_info, report.xbutton.button, report.xbutton.state); |
| #ifdef VERBOSE |
| if (button_info.shift_pressed == true) { |
| printf("Shift is pressed at button press.\n"); |
| } |
| if (button_info.ctrl_pressed == true) { |
| printf("Ctrl is pressed at button press.\n"); |
| } |
| #endif |
| switch (report.xbutton.button) { |
| case Button1: /* Left mouse click; pass back to client program */ |
| case Button3: /* Right mouse click; pass back to client program */ |
| /* Pass information about the button press to client program */ |
| if (act_on_mousebutton != NULL) // If callback was set |
| act_on_mousebutton(x, y, button_info); |
| break; |
| case Button2: /* Scroll wheel pressed; start panning */ |
| panning_on(report.xbutton.x, report.xbutton.y); |
| break; |
| case Button4: /* Scroll wheel rotated forward; screen does zoom_in */ |
| // note, this is also called in the skipping logic |
| handle_zoom_in(x, y, drawscreen); |
| break; |
| case Button5: /* Scroll wheel rotated backward; screen does zoom_out */ |
| // note, this is also called in the skipping logic |
| handle_zoom_out(x, y, drawscreen); |
| break; |
| default: |
| break; // Unknown button: ignore. |
| } |
| } else { /* A menu button was pressed. */ |
| bnum = x11_which_button(report.xbutton.window); |
| #ifdef VERBOSE |
| printf("Button number is %d\n", bnum); |
| #endif |
| if (button_state.button[bnum].enabled) { |
| button_state.button[bnum].ispressed = 1; |
| x11_drawbut(bnum); |
| XFlush(x11_state.display); /* Flash the button */ |
| button_state.button[bnum].fcn(drawscreen); |
| button_state.button[bnum].ispressed = 0; |
| x11_drawbut(bnum); |
| if (button_state.button[bnum].fcn == proceed) { |
| x11_turn_on_off(OFF); |
| flushinput(); |
| return; /* Rather clumsy way of returning * |
| * control to the simulator */ |
| } |
| } |
| } |
| break; |
| case ButtonRelease: |
| #ifdef VERBOSE |
| printf("Got a ButtonRelease.\n"); |
| printf("Window ID is: %ld.\n", report.xbutton.window); |
| printf("Button released is: %d.\n(left click is 1; right click is 3; " |
| "scroll wheel click is 2; scroll wheel forward rotate is 4; " |
| "scroll wheel backward is 5.)\n", report.xbutton.button); |
| printf("Mask is: %d.\n", report.xbutton.state); |
| #endif |
| switch (report.xbutton.button) { |
| case Button2: /* Scroll wheel released; stop panning */ |
| panning_off(); |
| break; |
| default: |
| break; |
| } |
| break; |
| case MotionNotify: |
| #ifdef VERBOSE |
| // printf("Got a MotionNotify Event.\n"); |
| // printf("x: %d y: %d\n", report.xmotion.x, report.xmotion.y); |
| #endif |
| if (pan_state.panning_enabled) |
| panning_execute(report.xmotion.x, report.xmotion.y, drawscreen); |
| else if (gl_state.get_mouse_move_input && |
| act_on_mousemove != NULL && |
| report.xmotion.x <= trans_coord.top_width - MWIDTH && |
| report.xmotion.y <= trans_coord.top_height - T_AREA_HEIGHT) |
| act_on_mousemove(xscrn_to_world(report.xmotion.x), yscrn_to_world(report.xmotion.y)); |
| break; |
| case KeyPress: |
| #ifdef VERBOSE |
| printf("Got a KeyPress Event.\n"); |
| #endif |
| if (gl_state.get_keypress_input) { |
| char keyb_buffer[20]; |
| XComposeStatus composestatus; |
| KeySym keysym; |
| int length, max_bytes; |
| |
| max_bytes = 1; |
| |
| length = XLookupString(&report.xkey, keyb_buffer, max_bytes, &keysym, |
| &composestatus); |
| #ifdef VERBOSE |
| cout << "char: " << keyb_buffer[0] << " char as int: " << (int) keyb_buffer[0] |
| << " keysym: " << keysym << endl; |
| #endif |
| |
| keyb_buffer[length] = '\0'; /* terminating NULL */ |
| if (act_on_keypress != NULL) |
| act_on_keypress(keyb_buffer[0], keysym); |
| } |
| |
| break; |
| case ClientMessage: |
| if (static_cast<int>(report.xclient.data.l[0]) == static_cast<int>(wmDeleteMessage)) { |
| // Close button has been clicked |
| #ifdef VERBOSE |
| std::cout << "Window close requested" << std::endl; |
| #endif |
| gl_state.ProceedPressed = true; |
| flushinput(); |
| return; |
| } |
| break; |
| |
| default: |
| break; // Ignore other events |
| } |
| } |
| } |
| |
| |
| // X11 drop redundant panning event explanation: |
| // When the user holds down the middle mouse button and pans, or scrolls |
| // using the scroll wheel, we can get events faster than we can redraw. |
| // This routine will drop events which match is_droppable_event(...), and are |
| // followed by another event which matches is_droppable_event(...). |
| // There are appropriate calls to XSync to make sure that this application |
| // has all of it's pending events in its queue. In X11, scrolls are |
| // like button presses - they have press *and release* events. There is |
| // logic to ignore the release event in the case where it's already dropped |
| // the press one. |
| // If this routine returns true, the event loop should drop an event (not |
| // process the event passed into this routine). If this routine returns false, |
| // the calling routine should process the event in report. |
| static bool x11_drop_redundant_panning (XEvent report, |
| unsigned int& last_skipped_button_press_button) { |
| |
| if (is_droppable_event(&report) && XQLength(x11_state.display) > 0) { |
| if (report.type == ButtonPress) { |
| last_skipped_button_press_button = report.xbutton.button; |
| } |
| // if the current event is droppable, and there are more events |
| // in the queue, check to see if the next event is droppable too. |
| XEvent next_event; |
| XPeekEvent(x11_state.display, &next_event); |
| // if the next event is a matching ButtonRelease, then drop |
| // it too, but only if the queue has more events still. |
| if (next_event.type == ButtonRelease |
| && next_event.xbutton.button == last_skipped_button_press_button |
| && XQLength(x11_state.display) > 1) { |
| |
| XSync(x11_state.display, false); |
| XNextEvent(x11_state.display, &next_event); |
| XPeekEvent(x11_state.display, &next_event); |
| last_skipped_button_press_button = -1; |
| } |
| if (is_droppable_event(&next_event)) { |
| // if so, skip this event. |
| if (report.type == ButtonPress) { |
| float x = xscrn_to_world(report.xbutton.x); |
| float y = yscrn_to_world(report.xbutton.y); |
| switch (report.xbutton.button) { |
| case Button4: |
| handle_zoom_in(x, y, NULL); // (same function as normal event logic) |
| break; |
| case Button5: |
| handle_zoom_out(x, y, NULL); // (same function as normal event logic) |
| break; |
| default: |
| // do nothing, also should be impossible |
| // (we don't want to skip mouse presses) |
| break; |
| } |
| } |
| return (true); // Calling routine should drop the event in report. |
| } |
| } |
| return (false); // Calling routine should process the event in report. |
| } |
| |
| |
| /* Creates a small window at the top of the graphics area for text messages */ |
| static void x11_build_textarea(void) { |
| XSetWindowAttributes menu_attributes; |
| unsigned long valuemask; |
| |
| x11_state.textarea = XCreateSimpleWindow(x11_state.display, x11_state.toplevel, 0, |
| trans_coord.top_height - T_AREA_HEIGHT, trans_coord.display_width - MWIDTH, |
| T_AREA_HEIGHT, 0, x11_convert_to_xcolor(t_color::predef_colors[BLACK]), |
| x11_convert_to_xcolor(t_color::predef_colors[LIGHTGREY])); |
| |
| x11_state.textarea_draw = XftDrawCreate( |
| x11_state.display, |
| x11_state.textarea, |
| x11_state.visual_info.visual, |
| x11_state.colormap_to_use |
| ); |
| |
| menu_attributes.event_mask = ExposureMask; |
| // menu_attributes.event_mask = 0; |
| /* ButtonPresses in this area are ignored. */ |
| menu_attributes.do_not_propagate_mask = ButtonPressMask; |
| /* Keep text area on bottom left */ |
| menu_attributes.win_gravity = SouthWestGravity; |
| valuemask = CWWinGravity | CWEventMask | CWDontPropagate; |
| XChangeWindowAttributes(x11_state.display, x11_state.textarea, valuemask, &menu_attributes); |
| XMapWindow(x11_state.display, x11_state.textarea); |
| } |
| |
| /* Returns True if the event passed in is an exposure event. Note that |
| * the bool type returned by this function is defined in Xlib.h. |
| */ |
| static Bool x11_test_if_exposed(Display *disp, XEvent *event_ptr, XPointer dummy) { |
| (void) disp; // suppress unused warning |
| (void) dummy; // suppress unused warning |
| if (event_ptr->type == Expose) { |
| return (True); |
| } |
| |
| return (False); |
| } |
| |
| /* draws UTF-8 text center at xc, yc -- used only by menu and button drawing stuff */ |
| static void menutext(XftDraw* draw, int xc, int yc, const char *text) { |
| int len, width; |
| |
| font_ptr menu_font = gl_state.font_info.get_font_info(MENU_FONT_SIZE, 0); |
| |
| len = strlen(text); |
| XGlyphInfo extents; |
| XftTextExtentsUtf8( |
| x11_state.display, |
| menu_font, |
| reinterpret_cast<const FcChar8*>(text), |
| len, |
| &extents |
| ); |
| width = extents.width; |
| |
| XftDrawStringUtf8( |
| draw, |
| &x11_state.xft_menutextcolor, |
| menu_font, |
| xc - width / 2, |
| yc + (menu_font->ascent |
| - menu_font->descent) / 2, |
| reinterpret_cast<const FcChar8*>(text), |
| len |
| ); |
| } |
| |
| /* Draws button bnum in either its pressed or unpressed state. */ |
| static void x11_drawbut(int bnum) { |
| int width, height, thick, i, ispressed; |
| XPoint mypoly[6]; |
| |
| width = button_state.button[bnum].width; |
| height = button_state.button[bnum].height; |
| |
| if (button_state.button[bnum].type == BUTTON_SEPARATOR) { |
| int x, y; |
| |
| x = button_state.button[bnum].xleft; |
| y = button_state.button[bnum].ytop; |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[WHITE])); |
| XDrawLine(x11_state.display, x11_state.menu, x11_state.gc_menus, x, y + 1, x + width, y + 1); |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK]) ); |
| XDrawLine(x11_state.display, x11_state.menu, x11_state.gc_menus, x, y, x + width, y); |
| return; |
| } |
| |
| ispressed = button_state.button[bnum].ispressed; |
| thick = 2; |
| /* Draw top and left edges of 3D box. */ |
| if (ispressed) { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK])); |
| } else { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[WHITE])); |
| } |
| |
| /* Note: X Windows doesn't appear to draw the bottom pixel of * |
| * a polygon with XFillPolygon, so I make this 1 pixel thicker * |
| * to compensate. */ |
| mypoly[0].x = 0; |
| mypoly[0].y = height; |
| mypoly[1].x = 0; |
| mypoly[1].y = 0; |
| mypoly[2].x = width; |
| mypoly[2].y = 0; |
| mypoly[3].x = width - thick; |
| mypoly[3].y = thick; |
| mypoly[4].x = thick; |
| mypoly[4].y = thick; |
| mypoly[5].x = thick; |
| mypoly[5].y = height - thick; |
| XFillPolygon(x11_state.display, button_state.button[bnum].win, x11_state.gc_menus, mypoly, 6, Convex, |
| CoordModeOrigin); |
| |
| /* Draw bottom and right edges of 3D box. */ |
| if (ispressed) { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[WHITE])); |
| } else { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK])); |
| } |
| mypoly[0].x = 0; |
| mypoly[0].y = height; |
| mypoly[1].x = width; |
| mypoly[1].y = height; |
| mypoly[2].x = width; |
| mypoly[2].y = 0; |
| mypoly[3].x = width - thick; |
| mypoly[3].y = thick; |
| mypoly[4].x = width - thick; |
| mypoly[4].y = height - thick; |
| mypoly[5].x = thick; |
| mypoly[5].y = height - thick; |
| XFillPolygon(x11_state.display, button_state.button[bnum].win, x11_state.gc_menus, mypoly, 6, Convex, |
| CoordModeOrigin); |
| |
| /* Draw background */ |
| if (ispressed) { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[DARKGREY]) |
| ); |
| } else { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[LIGHTGREY]) |
| ); |
| } |
| |
| /* Give x,y of top corner and width and height */ |
| XFillRectangle(x11_state.display, button_state.button[bnum].win, x11_state.gc_menus, thick, thick, |
| width - 2 * thick, height - 2 * thick); |
| |
| /* Draw polygon, if there is one */ |
| if (button_state.button[bnum].type == BUTTON_POLY) { |
| for (i = 0; i < 3; i++) { |
| mypoly[i].x = button_state.button[bnum].poly[i][0]; |
| mypoly[i].y = button_state.button[bnum].poly[i][1]; |
| } |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK]) |
| ); |
| XFillPolygon(x11_state.display, button_state.button[bnum].win, |
| x11_state.gc_menus, mypoly, 3, Convex, CoordModeOrigin); |
| } |
| |
| /* Draw text, if there is any */ |
| if (button_state.button[bnum].type == BUTTON_TEXT) { |
| if (button_state.button[bnum].enabled) { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK]) |
| ); |
| } else { |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[DARKGREY]) |
| ); |
| } |
| menutext(button_state.button[bnum].draw, button_state.button[bnum].width / 2, |
| button_state.button[bnum].height / 2, button_state.button[bnum].text); |
| } |
| } |
| |
| static int x11_which_button(Window win) { |
| int i; |
| |
| for (i = 0; i < button_state.num_buttons; i++) { |
| if (button_state.button[i].win == win) |
| return (i); |
| } |
| printf("Error: Unknown button ID in which_button.\n"); |
| return (0); |
| } |
| |
| /* Shows when the menu is active or inactive by colouring the |
| * buttons. |
| */ |
| static void x11_turn_on_off(int pressed) { |
| int i; |
| |
| for (i = 0; i < button_state.num_buttons; i++) { |
| button_state.button[i].ispressed = pressed; |
| x11_drawbut(i); |
| } |
| } |
| |
| static void x11_drawmenu(void) { |
| int i; |
| |
| XClearWindow(x11_state.display, x11_state.menu); |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[WHITE]) ); |
| XDrawRectangle(x11_state.display, x11_state.menu, x11_state.gc_menus, 0, 0, MWIDTH, |
| trans_coord.top_height); |
| XSetForeground(x11_state.display, x11_state.gc_menus, |
| x11_convert_to_xcolor(t_color::predef_colors[BLACK]) ); |
| XDrawLine(x11_state.display, x11_state.menu, x11_state.gc_menus, 0, trans_coord.top_height - 1, |
| MWIDTH, trans_coord.top_height - 1); |
| XDrawLine(x11_state.display, x11_state.menu, x11_state.gc_menus, MWIDTH - 1, |
| trans_coord.top_height, MWIDTH - 1, 0); |
| |
| for (i = 0; i < button_state.num_buttons; i++) { |
| x11_drawbut(i); |
| } |
| } |
| |
| static void x11_handle_expose(XEvent report, void (*drawscreen) (void)) { |
| #ifdef VERBOSE |
| printf("Got an expose event.\n"); |
| printf("Count is: %d.\n", report.xexpose.count); |
| printf("Window ID is: %ld.\n", report.xexpose.window); |
| #endif |
| |
| if (report.xexpose.count != 0) |
| return; // More exposes coming. |
| |
| gl_state.redraw_needed = true; // We will have to redraw eventually. |
| x11_redraw_all_if_needed (drawscreen); |
| } |
| |
| |
| static void x11_redraw_all_if_needed (void (*drawscreen) (void)) { |
| // Only redraw if this is (the last Expose or ConfigureNotify |
| // in a series (we sometimes get a lot for a window expose or size |
| // change, and some have a count of 0). We redraw everything for all |
| // exposes; trying to redraw only subwindows is problematic when we're |
| // dropping expose events. |
| if (XQLength(x11_state.display) > 0) { |
| // Check if we have events waiting, and peek at them only if we do |
| // XPeekEvent blocks if there are no events, and we don't want that. |
| XEvent next_event; |
| XPeekEvent(x11_state.display, &next_event); |
| if (next_event.type == Expose || next_event.type == ConfigureNotify) |
| return; |
| } |
| |
| // We could get here via a code path that doesn't need a redraw (no expose yet). |
| if (!gl_state.redraw_needed) |
| return; |
| |
| |
| #ifdef VERBOSE |
| cout << "Redrawing everything\n"; |
| #endif |
| |
| // resize window attributes |
| XGetWindowAttributes(x11_state.display, x11_state.toplevel, &(x11_state.attributes)); |
| // resize draw_buffer |
| XFreePixmap(x11_state.display, x11_state.draw_buffer); |
| XftDrawDestroy(x11_state.draw_buffer_draw); |
| x11_state.draw_buffer = XCreatePixmap(x11_state.display, x11_state.toplevel, |
| x11_state.attributes.width, x11_state.attributes.height, x11_state.attributes.depth); |
| x11_state.draw_buffer_draw = XftDrawCreate( |
| x11_state.display, |
| x11_state.draw_buffer, |
| x11_state.visual_info.visual, |
| x11_state.colormap_to_use |
| ); |
| // Update the pointers in the current drawing state to the new buffers and |
| // init_cairo again. |
| set_drawing_buffer(gl_state.current_draw_to); |
| |
| drawscreen(); |
| x11_drawmenu(); |
| draw_message(); |
| gl_state.redraw_needed = false; |
| } |
| |
| |
| static void x11_handle_configure_notify(XEvent report, void (*drawscreen) (void)) { |
| trans_coord.top_width = report.xconfigure.width; |
| trans_coord.top_height = report.xconfigure.height; |
| update_transform(); |
| |
| #ifdef VERBOSE |
| printf("Got a ConfigureNotify.\n"); |
| printf("New width: %d New height: %d.\n", trans_coord.top_width, trans_coord.top_height); |
| #endif |
| |
| x11_redraw_all_if_needed (drawscreen); |
| } |
| |
| static void x11_handle_button_info(t_event_buttonPressed *button_info, |
| int buttonNumber, int Xbutton_state) { |
| |
| button_info->button = buttonNumber; |
| |
| if (Xbutton_state & 1) |
| button_info->shift_pressed = true; |
| else |
| button_info->shift_pressed = false; |
| |
| if (Xbutton_state & 4) |
| button_info->ctrl_pressed = true; |
| else |
| button_info->ctrl_pressed = false; |
| } |
| |
| |
| static unsigned long x11_convert_to_xcolor (t_color rgb_color) { |
| unsigned long xcolor = 0; |
| uint_fast8_t red = rgb_color.red; |
| uint_fast8_t green = rgb_color.green; |
| uint_fast8_t blue = rgb_color.blue; |
| int cmp_bits = x11_state.visual_info.bits_per_rgb; |
| |
| xcolor |= (red << 2 * cmp_bits | red << cmp_bits | red) & x11_state.visual_info.red_mask; |
| xcolor |= (green << 2 * cmp_bits | green << cmp_bits | green) & x11_state.visual_info.green_mask; |
| xcolor |= (blue << 2 * cmp_bits | blue << cmp_bits | blue) & x11_state.visual_info.blue_mask; |
| |
| return (xcolor); |
| } |
| |
| #endif /* X-Windows Specific Definitions */ |
| |
| |
| /************************************************* |
| * Microsoft Windows (WIN32) Specific Definitions * |
| *************************************************/ |
| #ifdef WIN32 |
| |
| static void |
| win32_init_graphics(const char *window_name) { |
| WNDCLASSW wndclass; |
| HINSTANCE hInstance = GetModuleHandle(NULL); |
| int x, y; |
| LOGBRUSH lb; |
| lb.lbStyle = BS_SOLID; |
| lb.lbColor = convert_to_win_color(gl_state.foreground_color); |
| lb.lbHatch = (LONG) NULL; |
| x = 0; |
| y = 0; |
| |
| /* get screen size from display structure macro */ |
| trans_coord.display_width = GetSystemMetrics(SM_CXSCREEN); |
| if (!(trans_coord.display_width)) |
| WIN32_CREATE_ERROR(); |
| trans_coord.display_height = GetSystemMetrics(SM_CYSCREEN); |
| if (!(trans_coord.display_height)) |
| WIN32_CREATE_ERROR(); |
| trans_coord.top_width = 2 * trans_coord.display_width / 3; |
| trans_coord.top_height = 4 * trans_coord.display_height / 5; |
| |
| /* Copy the Application name */ |
| int text_byte_length = strlen(window_name); |
| MultiByteToWideChar(CP_UTF8, 0, window_name, -1, |
| szAppName, text_byte_length); |
| |
| win32_state.hGraphicsPen = ExtCreatePen( |
| PS_GEOMETRIC | win32_line_styles[gl_state.currentlinestyle] | PS_ENDCAP_FLAT, |
| 1, |
| &lb, |
| (LONG) NULL, |
| NULL |
| ); |
| |
| if (!win32_state.hGraphicsPen) |
| WIN32_CREATE_ERROR(); |
| win32_state.hGraphicsBrush = CreateSolidBrush(convert_to_win_color(t_color::predef_colors[DARKGREY])); |
| if (!win32_state.hGraphicsBrush) |
| WIN32_CREATE_ERROR(); |
| win32_state.hGrayBrush = CreateSolidBrush(convert_to_win_color(t_color::predef_colors[LIGHTGREY])); |
| if (!win32_state.hGrayBrush) |
| WIN32_CREATE_ERROR(); |
| |
| /* Register the Main Window class */ |
| wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; |
| wndclass.lpfnWndProc = WIN32_MainWND; |
| wndclass.cbClsExtra = 0; |
| wndclass.cbWndExtra = 0; |
| wndclass.hInstance = hInstance; |
| wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); |
| wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); |
| wndclass.hbrBackground = (HBRUSH) CreateSolidBrush( |
| convert_to_win_color(gl_state.background_color) |
| ); |
| wndclass.lpszMenuName = NULL; |
| wndclass.lpszClassName = szAppName; |
| |
| if (!RegisterClassW(&wndclass)) { |
| printf("Error code: %d\n", GetLastError()); |
| MessageBoxW(NULL, L"Initialization of Windows graphics (init_graphics) failed.", |
| szAppName, MB_ICONERROR); |
| exit(-1); |
| } |
| |
| /* Register the Graphics Window class */ |
| wndclass.lpfnWndProc = WIN32_GraphicsWND; |
| wndclass.hIcon = NULL; |
| wndclass.lpszClassName = szGraphicsName; |
| |
| if (!RegisterClassW(&wndclass)) |
| WIN32_DRAW_ERROR(); |
| |
| /* Register the Status Window class */ |
| wndclass.lpfnWndProc = WIN32_StatusWND; |
| wndclass.hIcon = NULL; |
| wndclass.lpszClassName = szStatusName; |
| wndclass.hbrBackground = win32_state.hGrayBrush; |
| |
| if (!RegisterClassW(&wndclass)) |
| WIN32_DRAW_ERROR(); |
| |
| /* Register the Buttons Window class */ |
| wndclass.lpfnWndProc = WIN32_ButtonsWND; |
| wndclass.hIcon = NULL; |
| wndclass.lpszClassName = szButtonsName; |
| wndclass.hbrBackground = win32_state.hGrayBrush; |
| |
| if (!RegisterClassW(&wndclass)) |
| WIN32_DRAW_ERROR(); |
| |
| win32_state.hMainWnd = CreateWindowW( |
| szAppName, szAppName, |
| WS_OVERLAPPEDWINDOW, x, y, trans_coord.top_width, |
| trans_coord.top_height, NULL, NULL, hInstance, NULL); |
| |
| if (!win32_state.hMainWnd) |
| WIN32_DRAW_ERROR(); |
| |
| /* Set drawing defaults for user-drawable area. Use whatever the * |
| * initial values of the current stuff was set to. */ |
| |
| if (ShowWindow(win32_state.hMainWnd, SW_SHOWNORMAL)) |
| WIN32_DRAW_ERROR(); |
| build_default_menu(); |
| if (!UpdateWindow(win32_state.hMainWnd)) |
| WIN32_DRAW_ERROR(); |
| win32_drain_message_queue(); |
| } |
| |
| static LRESULT CALLBACK |
| WIN32_MainWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { |
| MINMAXINFO FAR *lpMinMaxInfo; |
| |
| switch (message) { |
| case WM_CREATE: |
| win32_state.hStatusWnd = CreateWindowW(szStatusName, NULL, WS_CHILDWINDOW | WS_VISIBLE, |
| 0, 0, 0, 0, hwnd, (HMENU) 102, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL); |
| win32_state.hButtonsWnd = CreateWindowW(szButtonsName, NULL, WS_CHILDWINDOW | WS_VISIBLE, |
| 0, 0, 0, 0, hwnd, (HMENU) 103, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL); |
| win32_state.hGraphicsWnd = CreateWindowW(szGraphicsName, NULL, WS_CHILDWINDOW | WS_VISIBLE, |
| 0, 0, 0, 0, hwnd, (HMENU) 101, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL); |
| return 0; |
| |
| case WM_SIZE: |
| /* Window has been resized. Save the new client dimensions */ |
| trans_coord.top_width = LOWORD(lParam); |
| trans_coord.top_height = HIWORD(lParam); |
| |
| /* Resize the children windows */ |
| if (!MoveWindow(win32_state.hGraphicsWnd, 1, 1, trans_coord.top_width - MWIDTH - 1, |
| trans_coord.top_height - T_AREA_HEIGHT - 1, TRUE)) |
| WIN32_DRAW_ERROR(); |
| if (!MoveWindow(win32_state.hStatusWnd, 0, trans_coord.top_height - T_AREA_HEIGHT, |
| trans_coord.top_width - MWIDTH, T_AREA_HEIGHT, TRUE)) |
| WIN32_DRAW_ERROR(); |
| if (!MoveWindow(win32_state.hButtonsWnd, trans_coord.top_width - MWIDTH, 0, MWIDTH, |
| trans_coord.top_height, TRUE)) |
| WIN32_DRAW_ERROR(); |
| |
| return 0; |
| |
| // WC : added to solve window resizing problem |
| case WM_GETMINMAXINFO: |
| // set the MINMAXINFO structure pointer |
| lpMinMaxInfo = (MINMAXINFO FAR *) lParam; |
| lpMinMaxInfo->ptMinTrackSize.x = trans_coord.display_width / 2; |
| lpMinMaxInfo->ptMinTrackSize.y = trans_coord.display_height / 2; |
| |
| return 0; |
| |
| |
| case WM_DESTROY: |
| if (!DeleteObject(win32_state.hGrayBrush)) |
| WIN32_DELETE_ERROR(); |
| PostQuitMessage(0); |
| return 0; |
| |
| case WM_KEYDOWN: |
| if (gl_state.get_keypress_input && win32_keypress_ptr != NULL) |
| win32_keypress_ptr((char) wParam, 0); // TODO: add extended (keysym) codes |
| return 0; |
| |
| // Controls graphics: does zoom in or zoom out depending on direction of mousewheel scrolling. |
| // Only the window with the input focus will receive this message. In our case, the top-level |
| // window has the input focus, thus the code will not work if put in WIN32_GraphicsWND. |
| case WM_MOUSEWHEEL: |
| // t_event_buttonPressed is used as a structure for storing information about a mouse |
| // button press event. This information can be passed back to and used by a client |
| // program. |
| t_event_buttonPressed button_info; |
| win32_handle_button_info(button_info, message, wParam); |
| |
| win32_handle_mousewheel_zooming(wParam, lParam, true); |
| return 0; |
| case WM_TIMER: |
| win32_drawscreen_ptr(); |
| // fall out. |
| } |
| |
| return DefWindowProc(hwnd, message, wParam, lParam); |
| } |
| |
| static LRESULT CALLBACK |
| WIN32_GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { |
| static TEXTMETRIC tm; |
| |
| PAINTSTRUCT ps; |
| static RECT oldAdjustRect; |
| static HPEN hDotPen = 0; |
| static int X, Y, i; |
| |
| switch (message) { |
| case WM_CREATE: |
| |
| /* Get the text metrics once upon creation (system font cannot change) */ |
| win32_state.hGraphicsDC = GetDC(hwnd); |
| if (!win32_state.hGraphicsDC) |
| WIN32_DRAW_ERROR(); |
| |
| if (!SetBkMode(win32_state.hGraphicsDC, TRANSPARENT)) |
| WIN32_DRAW_ERROR(); |
| |
| /* Setup the pens, etc */ |
| gl_state.currentlinestyle = SOLID; |
| gl_state.foreground_color = t_color::predef_colors[BLACK]; |
| gl_state.currentlinewidth = 1; |
| gl_state.currentfontsize = 12; |
| return 0; |
| |
| case WM_PAINT: |
| win32_GraphicsWND_handle_WM_PAINT(hwnd, ps, hDotPen, oldAdjustRect); |
| return 0; |
| |
| case WM_SIZE: |
| /* Window has been resized. New client area dimensions can be retrieved from |
| * lParam using LOWORD() and HIWORD() macros. |
| */ |
| |
| update_transform(); |
| return 0; |
| |
| case WM_DESTROY: |
| if (!DeleteObject(win32_state.hGraphicsPen)) |
| WIN32_DELETE_ERROR(); |
| if (!DeleteObject(win32_state.hGraphicsBrush)) |
| WIN32_DELETE_ERROR(); |
| if (win32_state.hGraphicsFont != NULL && !DeleteObject(win32_state.hGraphicsFont)) |
| WIN32_DELETE_ERROR(); |
| PostQuitMessage(0); |
| return 0; |
| |
| // left click and right click have the same functionality |
| case WM_LBUTTONDOWN: |
| case WM_RBUTTONDOWN: |
| win32_GraphicsWND_handle_WM_LRBUTTONDOWN(message, wParam, lParam, X, Y, oldAdjustRect); |
| return 0; |
| |
| // If the mouse device has a scroll wheel, then this is a click of the wheel. |
| // Enable panning by holding down the mouse wheel and drag. |
| case WM_MBUTTONDOWN: |
| win32_GraphicsWND_handle_WM_MBUTTONDOWN(hwnd, message, wParam, lParam); |
| return 0; |
| |
| // Release the middle mouse button (mouse wheel) to stop panning |
| case WM_MBUTTONUP: |
| // turn off panning_enabled |
| panning_off(); |
| |
| /* Stops the mouse capturing started by SetCapture(). */ |
| ReleaseCapture(); |
| return 0; |
| |
| case WM_MOUSEMOVE: |
| win32_GraphicsWND_handle_WM_MOUSEMOVE(lParam, X, Y, oldAdjustRect); |
| return 0; |
| } |
| |
| return DefWindowProc(hwnd, message, wParam, lParam); |
| } |
| |
| static void |
| win32_GraphicsWND_handle_WM_PAINT(HWND hwnd, PAINTSTRUCT &ps, HPEN &hDotPen, RECT &oldAdjustRect) { |
| // was in xor mode, but got a general redraw. |
| // switch to normal drawing so we repaint properly. |
| if (gl_state.current_draw_mode == DRAW_XOR) { |
| set_draw_mode(DRAW_NORMAL); |
| win32_invalidate_screen(); |
| return; |
| } |
| BeginPaint(hwnd, &ps); |
| if (!win32_state.hGraphicsDC) |
| WIN32_DRAW_ERROR(); |
| |
| if (win32_state.InEventLoop) { |
| |
| // if program was still executing the "Window" command and drawing rubber band |
| if (win32_state.windowAdjustFlag == WAITING_FOR_SECOND_CORNER_POINT) { |
| /* ps.rcPaint specifies the screen coordinates of the window's client area |
| * in which drawing is requested. This information is used to indicate that |
| * the application window has been minimized and then restored. If so, need |
| * to redraw the screen before drawing new rubber band. |
| */ |
| if (ps.rcPaint.right == (trans_coord.top_width - MWIDTH - 1) |
| && ps.rcPaint.bottom == (trans_coord.top_height - T_AREA_HEIGHT - 1)) |
| win32_drawscreen_ptr(); |
| |
| // Create pen for rubber band drawing |
| hDotPen = CreatePen(PS_DASH, 1, convert_to_win_color(gl_state.background_color)); |
| if (!hDotPen) |
| WIN32_CREATE_ERROR(); |
| if (!SetROP2(win32_state.hGraphicsDC, R2_XORPEN)) |
| WIN32_SELECT_ERROR(); |
| if (!SelectObject(win32_state.hGraphicsDC, GetStockObject(NULL_BRUSH))) |
| WIN32_SELECT_ERROR(); |
| if (!SelectObject(win32_state.hGraphicsDC, hDotPen)) |
| WIN32_SELECT_ERROR(); |
| |
| // Don't need to erase old rubber band if the window has been minimized and |
| // restored, because previous drawings were invalidated when the window was |
| // minimized. |
| if (ps.rcPaint.right != (trans_coord.top_width - MWIDTH - 1) |
| || ps.rcPaint.bottom != (trans_coord.top_height - T_AREA_HEIGHT - 1)) { |
| // Erase old rubber band before drawing a new one |
| if (!Rectangle(win32_state.hGraphicsDC, oldAdjustRect.left, oldAdjustRect.top, |
| oldAdjustRect.right, oldAdjustRect.bottom)) |
| WIN32_DRAW_ERROR(); |
| } |
| |
| // Draw new rubber band |
| if (!Rectangle(win32_state.hGraphicsDC, win32_state.adjustRect.left, |
| win32_state.adjustRect.top, win32_state.adjustRect.right, |
| win32_state.adjustRect.bottom)) |
| WIN32_DRAW_ERROR(); |
| |
| oldAdjustRect = win32_state.adjustRect; |
| if (!SetROP2(win32_state.hGraphicsDC, R2_COPYPEN)) |
| WIN32_SELECT_ERROR(); |
| if (!SelectObject(win32_state.hGraphicsDC, GetStockObject(NULL_PEN))) |
| WIN32_SELECT_ERROR(); |
| if (!DeleteObject(hDotPen)) |
| WIN32_DELETE_ERROR(); |
| } else { |
| win32_drawscreen_ptr(); |
| } |
| } |
| EndPaint(hwnd, &ps); |
| |
| /* Crash hard if called at wrong time */ |
| /* win32_state.hGraphicsDC = NULL;*/ |
| } |
| |
| static void win32_GraphicsWND_handle_WM_LRBUTTONDOWN(UINT message, WPARAM wParam, LPARAM lParam, |
| int &X, int &Y, RECT &oldAdjustRect) { |
| // t_event_buttonPressed is used as a structure for storing information about a mouse |
| // button press event. This information can be passed back to and used by a client |
| // program. |
| t_event_buttonPressed button_info; |
| win32_handle_button_info(button_info, message, wParam); |
| |
| if (win32_state.windowAdjustFlag == WINDOW_DEACTIVATED) { |
| // Call function in client program if the callback was set |
| if (win32_mouseclick_ptr != NULL) |
| win32_mouseclick_ptr(xscrn_to_world(LOWORD(lParam)), yscrn_to_world(HIWORD(lParam)), |
| button_info); |
| } else { |
| // Special handling for the "Window" command, which takes multiple clicks. |
| // First you push the button, then you click for one corner, then you click for the other |
| // corner. |
| if (win32_state.windowAdjustFlag == WAITING_FOR_FIRST_CORNER_POINT) { |
| win32_state.windowAdjustFlag = WAITING_FOR_SECOND_CORNER_POINT; |
| X = win32_state.adjustRect.left = win32_state.adjustRect.right = LOWORD(lParam); |
| Y = win32_state.adjustRect.top = win32_state.adjustRect.bottom = HIWORD(lParam); |
| oldAdjustRect = win32_state.adjustRect; |
| } else { |
| int i; |
| int adjustx[2], adjusty[2]; |
| |
| win32_state.windowAdjustFlag = WINDOW_DEACTIVATED; |
| button_state.button[win32_state.adjustButton].ispressed = 0; |
| SendMessage(button_state.button[win32_state.adjustButton].hwnd, BM_SETSTATE, 0, 0); |
| |
| for (i = 0; i < button_state.num_buttons; i++) { |
| if (button_state.button[i].type != BUTTON_SEPARATOR |
| && button_state.button[i].enabled) { |
| if (!EnableWindow(button_state.button[i].hwnd, TRUE)) |
| WIN32_DRAW_ERROR(); |
| } |
| } |
| adjustx[0] = win32_state.adjustRect.left; |
| adjustx[1] = win32_state.adjustRect.right; |
| adjusty[0] = win32_state.adjustRect.top; |
| adjusty[1] = win32_state.adjustRect.bottom; |
| |
| update_win(adjustx, adjusty, win32_invalidate_screen); |
| } |
| } |
| } |
| |
| static void |
| win32_GraphicsWND_handle_WM_MBUTTONDOWN(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { |
| // t_event_buttonPressed is used as a structure for storing information about a mouse |
| // button press event. This information can be passed back to and used by a client |
| // program. |
| t_event_buttonPressed button_info; |
| win32_handle_button_info(button_info, message, wParam); |
| |
| // get x- and y- coordinates of the cursor. Do not use LOWORD or HIWORD macros |
| // to extract the coordinates because these macros can return incorrect results |
| // on systems with multiple monitors. |
| int xPos, yPos; |
| xPos = GET_X_LPARAM(lParam); |
| yPos = GET_Y_LPARAM(lParam); |
| |
| // turn on panning_enabled |
| panning_on(xPos, yPos); |
| |
| /* Windows function specifically designed for mouse click and drag. |
| * This function sends all mouse message to hGraphicsWnd, even if |
| * the cursor is outside the client program's top-level window. |
| */ |
| SetCapture(hwnd); |
| } |
| |
| static void |
| win32_GraphicsWND_handle_WM_MOUSEMOVE(LPARAM lParam, int &X, int &Y, RECT &oldAdjustRect) { |
| #ifdef VERBOSE |
| printf("Got a MotionNotify Event.\n"); |
| printf("x: %d y: %d\n", GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); |
| #endif |
| if (win32_state.windowAdjustFlag == WAITING_FOR_FIRST_CORNER_POINT) { |
| return; |
| } else if (win32_state.windowAdjustFlag == WAITING_FOR_SECOND_CORNER_POINT) { |
| if (X > LOWORD(lParam)) { |
| win32_state.adjustRect.left = LOWORD(lParam); |
| win32_state.adjustRect.right = X; |
| } else { |
| win32_state.adjustRect.left = X; |
| win32_state.adjustRect.right = LOWORD(lParam); |
| } |
| if (Y > HIWORD(lParam)) { |
| win32_state.adjustRect.top = HIWORD(lParam); |
| win32_state.adjustRect.bottom = Y; |
| } else { |
| win32_state.adjustRect.top = Y; |
| win32_state.adjustRect.bottom = HIWORD(lParam); |
| } |
| if (!InvalidateRect(win32_state.hGraphicsWnd, &oldAdjustRect, FALSE)) |
| WIN32_DRAW_ERROR(); |
| if (!InvalidateRect(win32_state.hGraphicsWnd, &win32_state.adjustRect, FALSE)) |
| WIN32_DRAW_ERROR(); |
| if (!UpdateWindow(win32_state.hGraphicsWnd)) |
| WIN32_DRAW_ERROR(); |
| |
| return; |
| } else if (pan_state.panning_enabled) { |
| // get x- and y- coordinates of the cursor. Do not use LOWORD or HIWORD macros |
| // to extract the coordinates because these macros can return incorrect results |
| // on systems with multiple monitors. |
| int xPos, yPos; |
| xPos = GET_X_LPARAM(lParam); |
| yPos = GET_Y_LPARAM(lParam); |
| |
| panning_execute(xPos, yPos, win32_drawscreen_ptr); |
| } else if (gl_state.get_mouse_move_input && win32_mousemove_ptr != NULL) { |
| win32_mousemove_ptr(xscrn_to_world(LOWORD(lParam)), yscrn_to_world(HIWORD(lParam))); |
| } |
| } |
| |
| static LRESULT CALLBACK |
| WIN32_StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { |
| HDC hdc; |
| PAINTSTRUCT ps; |
| RECT rect; |
| |
| switch (message) { |
| case WM_CREATE: |
| hdc = GetDC(hwnd); |
| if (!hdc) |
| WIN32_DRAW_ERROR(); |
| if (!SetBkMode(hdc, TRANSPARENT)) |
| WIN32_DRAW_ERROR(); |
| if (!ReleaseDC(hwnd, hdc)) |
| WIN32_DRAW_ERROR(); |
| return 0; |
| |
| case WM_PAINT: |
| { |
| hdc = BeginPaint(hwnd, &ps); |
| if (!hdc) |
| WIN32_DRAW_ERROR(); |
| |
| if (!GetClientRect(hwnd, &rect)) |
| WIN32_DRAW_ERROR(); |
| |
| if (!SelectObject(hdc, GetStockObject(NULL_BRUSH))) |
| WIN32_SELECT_ERROR(); |
| if (!SelectObject(hdc, GetStockObject(WHITE_PEN))) |
| WIN32_SELECT_ERROR(); |
| if (!Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom)) |
| WIN32_DRAW_ERROR(); |
| if (!SelectObject(hdc, GetStockObject(BLACK_PEN))) |
| WIN32_SELECT_ERROR(); |
| if (!MoveToEx(hdc, rect.left, rect.bottom - 1, NULL)) |
| WIN32_DRAW_ERROR(); |
| if (!LineTo(hdc, rect.right - 1, rect.bottom - 1)) |
| WIN32_DRAW_ERROR(); |
| if (!LineTo(hdc, rect.right - 1, rect.top)) |
| WIN32_DRAW_ERROR(); |
| |
| int text_byte_length = strlen(gl_state.statusMessage); |
| wchar_t* WIN32_wchar_text = new wchar_t[text_byte_length]; |
| size_t WIN32_wchar_text_len = |
| MultiByteToWideChar( |
| CP_UTF8, 0, |
| gl_state.statusMessage, text_byte_length, |
| WIN32_wchar_text, text_byte_length |
| ); |
| |
| if (!DrawTextW(hdc, WIN32_wchar_text, WIN32_wchar_text_len, &rect, |
| DT_CENTER | DT_VCENTER | DT_SINGLELINE)) |
| WIN32_DRAW_ERROR(); |
| |
| delete[] WIN32_wchar_text; |
| |
| if (!EndPaint(hwnd, &ps)) |
| WIN32_DRAW_ERROR(); |
| return 0; |
| |
| } |
| case WM_SIZE: |
| return 0; |
| |
| case WM_DESTROY: |
| PostQuitMessage(0); |
| return 0; |
| } |
| |
| return DefWindowProc(hwnd, message, wParam, lParam); |
| } |
| |
| static LRESULT CALLBACK |
| WIN32_ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { |
| HDC hdc; |
| PAINTSTRUCT ps; |
| RECT rect; |
| static HBRUSH hBrush; |
| int i; |
| |
| switch (message) { |
| case WM_COMMAND: |
| if (win32_state.windowAdjustFlag == WINDOW_DEACTIVATED) { |
| button_state.button[LOWORD(wParam) - 200].fcn(win32_invalidate_screen); |
| if (win32_state.windowAdjustFlag != WINDOW_DEACTIVATED) { |
| win32_state.adjustButton = LOWORD(wParam) - 200; |
| button_state.button[win32_state.adjustButton].ispressed = 1; |
| for (i = 0; i < button_state.num_buttons; i++) { |
| EnableWindow(button_state.button[i].hwnd, FALSE); |
| SendMessage(button_state.button[i].hwnd, BM_SETSTATE, |
| button_state.button[i].ispressed, 0); |
| } |
| } |
| } |
| SetFocus(win32_state.hMainWnd); |
| return 0; |
| |
| case WM_CREATE: |
| hdc = GetDC(hwnd); |
| if (!hdc) |
| WIN32_DRAW_ERROR(); |
| hBrush = CreateSolidBrush(convert_to_win_color(t_color::predef_colors[LIGHTGREY])); |
| if (!hBrush) |
| WIN32_CREATE_ERROR(); |
| if (!SelectObject(hdc, hBrush)) |
| WIN32_SELECT_ERROR(); |
| if (!SetBkMode(hdc, TRANSPARENT)) |
| WIN32_DRAW_ERROR(); |
| if (!ReleaseDC(hwnd, hdc)) |
| WIN32_DRAW_ERROR(); |
| |
| return 0; |
| |
| case WM_PAINT: |
| hdc = BeginPaint(hwnd, &ps); |
| if (!hdc) |
| WIN32_DRAW_ERROR(); |
| |
| if (!GetClientRect(hwnd, &rect)) |
| WIN32_DRAW_ERROR(); |
| |
| if (!SelectObject(hdc, GetStockObject(NULL_BRUSH))) |
| WIN32_SELECT_ERROR(); |
| if (!SelectObject(hdc, GetStockObject(WHITE_PEN))) |
| WIN32_SELECT_ERROR(); |
| if (!Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom)) |
| WIN32_DRAW_ERROR(); |
| if (!SelectObject(hdc, GetStockObject(BLACK_PEN))) |
| WIN32_SELECT_ERROR(); |
| if (!MoveToEx(hdc, rect.left, rect.bottom - 1, NULL)) |
| WIN32_DRAW_ERROR(); |
| if (!LineTo(hdc, rect.right - 1, rect.bottom - 1)) |
| WIN32_DRAW_ERROR(); |
| if (!LineTo(hdc, rect.right - 1, rect.top)) |
| WIN32_DRAW_ERROR(); |
| |
| for (i = 0; i < button_state.num_buttons; i++) { |
| if (button_state.button[i].type == BUTTON_SEPARATOR) { |
| int x, y, w; |
| |
| x = button_state.button[i].xleft; |
| y = button_state.button[i].ytop; |
| w = button_state.button[i].width; |
| |
| if (!MoveToEx(hdc, x, y, NULL)) |
| WIN32_DRAW_ERROR(); |
| if (!LineTo(hdc, x + w, y)) |
| WIN32_DRAW_ERROR(); |
| if (!SelectObject(hdc, GetStockObject(WHITE_PEN))) |
| WIN32_SELECT_ERROR(); |
| if (!MoveToEx(hdc, x, y + 1, NULL)) |
| WIN32_DRAW_ERROR(); |
| if (!LineTo(hdc, x + w, y + 1)) |
| WIN32_DRAW_ERROR(); |
| if (!SelectObject(hdc, GetStockObject(BLACK_PEN))) |
| WIN32_SELECT_ERROR(); |
| } |
| } |
| if (!EndPaint(hwnd, &ps)) |
| WIN32_DRAW_ERROR(); |
| return 0; |
| |
| case WM_DESTROY: |
| for (i = 0; i < button_state.num_buttons; i++) { |
| } |
| if (!DeleteObject(hBrush)) |
| WIN32_DELETE_ERROR(); |
| PostQuitMessage(0); |
| return 0; |
| } |
| |
| return DefWindowProc(hwnd, message, wParam, lParam); |
| } |
| |
| static void WIN32_SELECT_ERROR() { |
| wchar_t msg[BUFSIZE]; |
| wsprintf(msg, L"Error %i: Couldn't select graphics object on line %d of graphics.c\n", |
| GetLastError(), __LINE__); |
| |
| wprintf(msg); |
| MessageBoxW(NULL, msg, NULL, MB_OK); |
| exit(-1); |
| } |
| |
| static void WIN32_DELETE_ERROR() { |
| wchar_t msg[BUFSIZE]; |
| wsprintf(msg, L"Error %i: Couldn't delete graphics object on line %d of graphics.c\n", |
| GetLastError(), __LINE__); |
| |
| wprintf(msg); |
| MessageBoxW(NULL, msg, NULL, MB_OK); |
| exit(-1); |
| } |
| |
| static void WIN32_CREATE_ERROR() { |
| wchar_t msg[BUFSIZE]; |
| wsprintf(msg, L"Error %i: Couldn't create graphics object on line %d of graphics.c\n", |
| GetLastError(), __LINE__); |
| |
| wprintf(msg); |
| MessageBoxW(NULL, msg, NULL, MB_OK); |
| exit(-1); |
| } |
| |
| static void WIN32_DRAW_ERROR() { |
| wchar_t msg[BUFSIZE]; |
| wsprintf(msg, L"Error %i: Couldn't draw graphics object on line %d of graphics.c\n", |
| GetLastError(), __LINE__); |
| |
| wprintf(msg); |
| MessageBoxW(NULL, msg, NULL, MB_OK); |
| exit(-1); |
| } |
| |
| static void win32_invalidate_screen(void) { |
| /* Tells the graphics engine to redraw the graphics display since information has changed */ |
| |
| if (!InvalidateRect(win32_state.hGraphicsWnd, NULL, FALSE)) |
| WIN32_DRAW_ERROR(); |
| if (!UpdateWindow(win32_state.hGraphicsWnd)) |
| WIN32_DRAW_ERROR(); |
| } |
| |
| static void win32_reset_state() { |
| // Not sure exactly what needs to be reset to NULL etc. |
| // Resetting everthing to be safe. |
| win32_state.hGraphicsPen = 0; |
| win32_state.hGraphicsBrush = 0; |
| win32_state.hGrayBrush = 0; |
| win32_state.hGraphicsDC = 0; |
| win32_state.hGraphicsFont = 0; |
| |
| /* These are used for the "Window" graphics button. They keep track of whether we're entering |
| * the window rectangle to zoom to, etc. |
| */ |
| win32_state.windowAdjustFlag = WINDOW_DEACTIVATED; |
| win32_state.adjustButton = -1; |
| win32_state.InEventLoop = false; |
| } |
| |
| static void win32_drain_message_queue() { |
| // Drain the message queue, so we don't have a quit message lying around |
| // that will stop us from re-opening a window later if desired. |
| MSG msg; |
| while (PeekMessage(&msg, win32_state.hMainWnd, 0, 0, PM_REMOVE)) { |
| if (msg.message == WM_QUIT) { |
| printf("Got the quit message.\n"); |
| } |
| } |
| } |
| |
| static void win32_handle_mousewheel_zooming(WPARAM wParam, LPARAM lParam, bool draw_screen) { |
| // zDelta indicates the distance which the mouse wheel is rotated. |
| // The value for zDelta is a multiple of WHEEL_DELTA, which is 120. |
| // WHEEL_DELTA is the value for scrolling the mouse wheel by one increment. |
| short zDelta; |
| zDelta = GET_WHEEL_DELTA_WPARAM(wParam); |
| |
| // roll_detent captures how many increments the mouse wheel has been scrolled by. |
| int roll_detent; |
| roll_detent = zDelta / WHEEL_DELTA; |
| // roll_detent will be negative if wheel was roatated backward, toward the user. |
| if (roll_detent < 0) |
| roll_detent *= -1; |
| |
| // get x- and y- coordinates of the cursor. WM_MOUSEWHEEL receives coordinates of |
| // the cursor relative to the upper-left corner of the screen instead of the client |
| // program. Therefore, call ScreenToClient() to convert. |
| POINT mousePos; |
| mousePos.x = GET_X_LPARAM(lParam); |
| mousePos.y = GET_Y_LPARAM(lParam); |
| ScreenToClient(win32_state.hMainWnd, &mousePos); |
| |
| |
| for (int i = 1; i <= roll_detent; i++) { |
| // Positive value for zDelta indicates that the wheel was rotated forward, which |
| // will trigger zoom_in. Otherwise, zoom_out is called. |
| // Also, only redraw the screen on the last one. |
| if (zDelta > 0) |
| handle_zoom_in(xscrn_to_world(mousePos.x), yscrn_to_world(mousePos.y), |
| (draw_screen && (i == roll_detent)) ? win32_drawscreen_ptr : NULL); |
| else |
| handle_zoom_out(xscrn_to_world(mousePos.x), yscrn_to_world(mousePos.y), |
| (draw_screen && (i == roll_detent)) ? win32_drawscreen_ptr : NULL); |
| } |
| } |
| |
| static void win32_handle_button_info(t_event_buttonPressed &button_info, |
| UINT message, WPARAM wParam) { |
| /* The parameter "wParam" is an unsigned int. In this case, it contains information indicating * |
| * whether various virtual keys (ie. modifier keys) are held during a mouse button press * |
| * event. */ |
| if (wParam & MK_SHIFT) |
| button_info.shift_pressed = true; |
| else |
| button_info.shift_pressed = false; |
| |
| if (wParam & MK_CONTROL) |
| button_info.ctrl_pressed = true; |
| else |
| button_info.ctrl_pressed = false; |
| |
| /* Parameter "message" indicates what button is pressed: pass 1 for left click, |
| * 3 for right click, 2 for scroll wheel click, 4 for scroll wheel forward rotate, |
| * and 5 for scroll wheel backward rotate. |
| * We follow this convention in order to be consistent for both X11 and WIN32. |
| */ |
| switch (message) { |
| case (WM_LBUTTONDOWN): |
| button_info.button = 1; |
| break; |
| case (WM_RBUTTONDOWN): |
| button_info.button = 3; |
| break; |
| case (WM_MBUTTONDOWN): |
| button_info.button = 2; |
| break; |
| case (WM_MOUSEWHEEL): |
| short zDelta; |
| zDelta = GET_WHEEL_DELTA_WPARAM(wParam); |
| // Positive value for zDelta indicates that the wheel was rotated forward, |
| // away from user, and negative value indicates wheel rotated backward. |
| if (zDelta > 0) |
| button_info.button = 4; |
| else |
| button_info.button = 5; |
| break; |
| } |
| |
| #ifdef VERBOSE |
| printf("Button pressed is: %d.\n(left click is 1; right click is 3; " |
| "scroll wheel click is 2; scroll wheel forward rotate is 4; " |
| "scroll wheel backward is 5.)\n", button_info.button); |
| if (button_info.shift_pressed == true) |
| printf("Shift is pressed at button press.\n"); |
| if (button_info.ctrl_pressed == true) |
| printf("Ctrl is pressed at button press.\n"); |
| #endif |
| } |
| |
| static void _drawcurve(t_point *points, int npoints, int fill) { |
| /* Draw a beizer curve. |
| * Must have 3I+1 points, since each Beizer curve needs 3 points and we also |
| * need an initial starting point |
| */ |
| float xmin, ymin, xmax, ymax; |
| int i; |
| |
| #define MAXPTS 300 |
| |
| if ((npoints - 1) % 3 != 0 || npoints > MAXPTS) |
| WIN32_DRAW_ERROR(); |
| |
| /* Conservative (but fast) clip test -- check containing rectangle of * |
| * polygon. */ |
| |
| xmin = xmax = points[0].x; |
| ymin = ymax = points[0].y; |
| |
| for (i = 1; i < npoints; i++) { |
| xmin = min(xmin, points[i].x); |
| xmax = max(xmax, points[i].x); |
| ymin = min(ymin, points[i].y); |
| ymax = max(ymax, points[i].y); |
| } |
| |
| if (rect_off_screen(xmin, ymin, xmax, ymax)) |
| return; |
| |
| if (gl_state.disp_type == SCREEN) { |
| #ifdef X11 |
| /* implement X11 version here */ |
| #else /* Win32 */ |
| // create POINT array |
| HPEN hOldPen; |
| POINT pts[MAXPTS]; |
| int i; |
| |
| for (i = 0; i < npoints; i++) { |
| pts[i].x = xworld_to_scrn(points[i].x); |
| pts[i].y = yworld_to_scrn(points[i].y); |
| } |
| |
| if (fill) { |
| /* NULL_PEN is a Windows stock object which does not draw anything. Set current * |
| * pen to NULL_PEN in order to fill the curve without drawing the outline. */ |
| hOldPen = (HPEN) SelectObject(win32_state.hGraphicsDC, GetStockObject(NULL_PEN)); |
| if (!(hOldPen)) |
| WIN32_SELECT_ERROR(); |
| } |
| |
| if (!BeginPath(win32_state.hGraphicsDC)) |
| WIN32_DRAW_ERROR(); |
| if (!PolyBezier(win32_state.hGraphicsDC, pts, npoints)) |
| WIN32_DRAW_ERROR(); |
| if (!EndPath(win32_state.hGraphicsDC)) |
| WIN32_DRAW_ERROR(); |
| |
| if (!fill) { |
| if (!StrokePath(win32_state.hGraphicsDC)) |
| WIN32_DRAW_ERROR(); |
| } else { |
| if (!FillPath(win32_state.hGraphicsDC)) |
| WIN32_DRAW_ERROR(); |
| } |
| |
| if (fill) { |
| /* Need to restore the original pen into the device context after filling. */ |
| if (!SelectObject(win32_state.hGraphicsDC, hOldPen)) |
| WIN32_SELECT_ERROR(); |
| } |
| #endif |
| } else { |
| int i; |
| |
| fprintf(gl_state.ps, "newpath\n"); |
| fprintf(gl_state.ps, "%.2f %.2f moveto\n", x_to_post(points[0].x), |
| y_to_post(points[0].y)); |
| for (i = 1; i < npoints; i += 3) |
| fprintf(gl_state.ps, "%.2f %.2f %.2f %.2f %.2f %.2f curveto\n", |
| x_to_post(points[i].x), yworld_to_post(points[i].y), |
| x_to_post(points[i + 1].x), y_to_post(points[i + 1].y), |
| x_to_post(points[i + 2].x), y_to_post(points[i + 2].y)); |
| if (!fill) |
| fprintf(gl_state.ps, "stroke\n"); |
| else |
| fprintf(gl_state.ps, "fill\n"); |
| } |
| } |
| |
| void win32_drawcurve(t_point *points, |
| int npoints) { |
| _drawcurve(points, npoints, 0); |
| } |
| |
| void win32_fillcurve(t_point *points, |
| int npoints) { |
| _drawcurve(points, npoints, 1); |
| } |
| |
| #endif /******** Win32 Specific Definitions ********************/ |
| |
| |
| #else /***** NO_GRAPHICS *******/ |
| /* No graphics at all. Stub everything out so calling program doesn't have to change |
| * but of course graphics won't do anything. |
| */ |
| |
| #include "graphics.h" |
| |
| void event_loop(void (* /*act_on_mousebutton*/) (float x, float y, t_event_buttonPressed button_info), |
| void (* /*act_on_mousemove*/) (float x, float y), |
| void (* /*act_on_keypress*/) (char key_pressed, int keysym), |
| void (* /*drawscreen*/) (void)) { } |
| |
| void init_graphics(const std::string& /*window_name*/, int /*cindex*/) { } |
| |
| void init_graphics(const std::string& /*window_name*/, const t_color& /*background*/) { } |
| |
| void close_graphics(void) { } |
| |
| void update_message(const std::string& /*msg*/) { } |
| |
| void draw_message(void) { } |
| |
| void set_visible_world(float /*xl*/, float /*yt*/, float /*xr*/, float /*yb*/) { } |
| |
| void set_visible_world(const t_bound_box& /*bounds*/) { } |
| |
| void flushinput(void) { } |
| |
| void setcolor(int /*cindex*/) { } |
| |
| void setcolor(const t_color& /*c*/) { } |
| |
| void setcolor(uint_fast8_t /*r*/, uint_fast8_t /*g*/, uint_fast8_t /*b*/, uint_fast8_t /*a*/) { } |
| |
| void setcolor_by_name(std::string /*cname*/) { } |
| |
| t_color getcolor(void) { |
| return t_color(0, 0, 0); |
| } |
| |
| void setlinewidth(int /*linewidth*/) { } |
| |
| void setfontsize(int /*pointsize*/) { } |
| |
| int getfontsize() { |
| return 0; |
| } |
| |
| void settextrotation(int /*degrees*/) { } |
| |
| int gettextrotation() { |
| return 0; |
| } |
| |
| void settextattrs(int /*pointsize*/, int /*degrees*/) { } |
| |
| void drawline(const t_point& /*p1*/, const t_point& /*p2*/) { } |
| |
| void drawline(float /*x1*/, float /*y1*/, float /*x2*/, float /*y2*/) { } |
| |
| void drawrect(const t_bound_box& /*rect*/) { } |
| |
| void drawrect(const t_point& /*bottomleft*/, const t_point& /*upperright*/) { } |
| |
| void drawrect(float /*x1*/, float /*y1*/, float /*x2*/, float /*y2*/) { } |
| |
| void fillrect(const t_bound_box& /*rect*/) { } |
| |
| void fillrect(const t_point& /*bottomleft*/, const t_point& /*upperright*/) { } |
| |
| void fillrect(float /*x1*/, float /*y1*/, float /*x2*/, float /*y2*/) { } |
| |
| void fillpoly(t_point* /*points*/, int /*npoints*/) { } |
| |
| void drawarc(float /*xcen*/, float /*ycen*/, float /*rad*/, float /*startang*/, |
| float /*angextent*/) { } |
| |
| void drawellipticarc( |
| const t_point& /*center*/, float /*radx*/, float /*rady*/, float /*startang*/, float /*angextent*/) { } |
| |
| void drawellipticarc(float /*xc*/, float /*yc*/, float /*radx*/, float /*rady*/, |
| float /*startang*/, float /*angextent*/) { } |
| |
| void fillarc(const t_point& /*center*/, float /*rad*/, float /*startang*/, float /*angextent*/) { } |
| |
| void fillarc(float /*xcen*/, float /*ycen*/, float /*rad*/, float /*startang*/, |
| float /*angextent*/) { } |
| |
| void fillellipticarc( |
| const t_point& /*center*/, float /*radx*/, float /*rady*/, float /*startang*/, float /*angextent*/) { } |
| |
| void fillellipticarc(float /*xc*/, float /*yc*/, float /*radx*/, float /*rady*/, |
| float /*startang*/, float /*angextent*/) { } |
| |
| void drawtext_in(const t_bound_box& /*bbox*/, const std::string& /*text*/) { } |
| |
| void drawtext_in(const t_bound_box& /*bbox*/, const std::string& /*text*/, float /*tolerance*/) { } |
| |
| void drawtext(const t_point& /*text_center*/, const std::string& /*text*/, const t_bound_box& /*bounds*/) { } |
| |
| void drawtext(const t_point& /*text_center*/, const std::string& /*text*/, const t_bound_box& /*bounds*/, float /*tolerance*/) { } |
| |
| void drawtext(const t_point& /*text_center*/, const std::string& /*text*/, float /*boundx*/, float /*boundy*/) { } |
| |
| void drawtext(float /*xc*/, float /*yc*/, const std::string& /*text*/, float /*boundx*/, float /*boundy*/) { } |
| |
| void clearscreen(void) { } |
| |
| t_bound_box get_visible_world() { |
| return t_bound_box(0, 0, 0, 0); |
| } |
| |
| t_bound_box get_visible_screen() { |
| return (t_bound_box(0, 0, 0, 0)); |
| } |
| |
| void create_button(const char* /*prev_button_text*/, const char* /*button_text*/, |
| void (* /*button_func*/) (void (*drawscreen) (void))) { } |
| |
| void destroy_button(const char* /*button_text*/) { } |
| |
| int init_postscript(const char* /*fname*/) { |
| return (1); |
| } |
| |
| void close_postscript(void) { } |
| |
| void get_report_structure(t_report*) { } |
| |
| void set_mouse_move_input(bool) { } |
| |
| void set_keypress_input(bool) { } |
| |
| void set_draw_mode(enum e_draw_mode /*draw_mode*/) { } |
| |
| void enable_or_disable_button(int /*ibutton*/, bool /*enabled*/) { } |
| |
| void change_button_text(const char* /*button_text*/, const char* /*new_button_text*/) { } |
| |
| |
| t_point world_to_scrn(const t_point& /*point*/) { |
| return t_point(); |
| } |
| |
| t_bound_box world_to_scrn(const t_bound_box& /*box*/) { |
| return t_bound_box(); |
| } |
| |
| t_point scrn_to_world(const t_point& /*point*/) { |
| return t_point(); |
| } |
| |
| t_bound_box scrn_to_world(const t_bound_box& /*box*/) { |
| return t_bound_box(); |
| } |
| |
| bool LOD_screen_area_test(t_bound_box /*test*/, float /*screen_area_threshold*/) { |
| return true; |
| } |
| |
| void setlinestyle(int /*linestyle*/, int /*capstyle*/) { } |
| void set_drawing_buffer(t_draw_to /*draw_mode*/) { } |
| void copy_off_screen_buffer_to_screen() { } |
| |
| #ifdef WIN32 |
| |
| void win32_drawcurve(t_point* /*points*/, int npoints) { |
| } |
| |
| void win32_fillcurve(t_point* points, int npoints) { |
| } |
| |
| |
| #endif // WIN32 (subset of commands) |
| |
| #endif // NO_GRAPHICS |
| |
| |
| #ifdef WIN32 |
| |
| COLORREF convert_to_win_color(const t_color& src) { |
| return RGB(src.red, src.green, src.blue); |
| } |
| #endif /* WIN32 */ |
| |
| |