lab4 start
This commit is contained in:
21
Lab4/drawbase.h
Normal file
21
Lab4/drawbase.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef DRAWBASE_H
|
||||||
|
#define DRAWBASE_H
|
||||||
|
|
||||||
|
// forward reference
|
||||||
|
class GraphicsContext;
|
||||||
|
|
||||||
|
class DrawingBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// prevent warnings
|
||||||
|
virtual ~DrawingBase(){}
|
||||||
|
virtual void paint(GraphicsContext* gc){}
|
||||||
|
virtual void keyDown(GraphicsContext* gc, unsigned int keycode){}
|
||||||
|
virtual void keyUp(GraphicsContext* gc, unsigned int keycode){}
|
||||||
|
virtual void mouseButtonDown(GraphicsContext* gc,
|
||||||
|
unsigned int button, int x, int y){}
|
||||||
|
virtual void mouseButtonUp(GraphicsContext* gc,
|
||||||
|
unsigned int button, int x, int y){}
|
||||||
|
virtual void mouseMove(GraphicsContext* gc, int x, int y){}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
26
Lab4/gcontext.cpp
Normal file
26
Lab4/gcontext.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/* This is an abstract base class representing a generic graphics
|
||||||
|
* context. Most implementation specifics will need to be provided by
|
||||||
|
* a concrete implementation. See header file for specifics. */
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES // for M_PI
|
||||||
|
#include <cmath> // for trig functions
|
||||||
|
#include "gcontext.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Destructor - does nothing
|
||||||
|
*/
|
||||||
|
GraphicsContext::~GraphicsContext()
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
// here to insure subclasses handle destruction properly
|
||||||
|
}
|
||||||
|
|
||||||
|
//does nothing
|
||||||
|
void GraphicsContext::drawLine(int x0, int y0, int x1, int y1){}
|
||||||
|
void GraphicsContext::drawCircle(int x0, int y0, unsigned int radius){}
|
||||||
|
|
||||||
|
|
||||||
|
void GraphicsContext::endLoop()
|
||||||
|
{
|
||||||
|
run = false;
|
||||||
|
}
|
||||||
141
Lab4/gcontext.h
Normal file
141
Lab4/gcontext.h
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#ifndef GCONTEXT_H
|
||||||
|
#define GCONTEXT_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is intended to be the abstract base class
|
||||||
|
* for a graphical context for various platforms. Any
|
||||||
|
* concrete subclass will need to implement the pure virtual
|
||||||
|
* methods to support setting pixels, getting pixel color,
|
||||||
|
* setting the drawing mode, and running an event loop to
|
||||||
|
* capture mouse and keyboard events directed to the graphics
|
||||||
|
* context (or window). Specific expectations for the various
|
||||||
|
* methods are documented below.
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
|
||||||
|
// forward reference - needed because runLoop needs a target for events
|
||||||
|
class DrawingBase;
|
||||||
|
|
||||||
|
|
||||||
|
class GraphicsContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*********************************************************
|
||||||
|
* Some constants and enums
|
||||||
|
*********************************************************/
|
||||||
|
// This enumerated type is an argument to setMode and allows
|
||||||
|
// us to support two different drawing modes. MODE_NORMAL is
|
||||||
|
// also call copy-mode and the affect pixel(s) are set to the
|
||||||
|
// color requested. XOR mode will XOR the new color with the
|
||||||
|
// existing color so that the change is reversible.
|
||||||
|
enum drawMode {MODE_NORMAL, MODE_XOR};
|
||||||
|
|
||||||
|
// Some colors - for fun
|
||||||
|
static const unsigned int BLACK = 0x000000;
|
||||||
|
static const unsigned int BLUE = 0x0000FF;
|
||||||
|
static const unsigned int GREEN = 0x00FF00;
|
||||||
|
static const unsigned int RED = 0xFF0000;
|
||||||
|
static const unsigned int CYAN = 0x00FFFF;
|
||||||
|
static const unsigned int MAGENTA = 0xFF00FF;
|
||||||
|
static const unsigned int YELLOW = 0xFFFF00;
|
||||||
|
static const unsigned int GRAY = 0x808080;
|
||||||
|
static const unsigned int WHITE = 0xFFFFFF;
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* Construction / Destruction
|
||||||
|
*********************************************************/
|
||||||
|
// Implementations of this class should include a constructor
|
||||||
|
// that creates the drawing canvas (window), sets a background
|
||||||
|
// color (which may be configurable), sets a default drawing
|
||||||
|
// color (which may be configurable), and start with normal
|
||||||
|
// (copy) drawing mode.
|
||||||
|
|
||||||
|
// need a virtual destructor to ensure subclasses will have
|
||||||
|
// their destructors called properly. Must be virtual.
|
||||||
|
virtual ~GraphicsContext();
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* Drawing operations
|
||||||
|
*********************************************************/
|
||||||
|
|
||||||
|
// Allows the drawing mode to be changed between normal (copy)
|
||||||
|
// and xor. The implementing context should default to normal.
|
||||||
|
virtual void setMode(drawMode newMode) = 0;
|
||||||
|
|
||||||
|
// Set the current color. Implementations should default to white.
|
||||||
|
// color is 24-bit RGB value
|
||||||
|
virtual void setColor(unsigned int color) = 0;
|
||||||
|
|
||||||
|
// Set pixel to the current color
|
||||||
|
virtual void setPixel(int x, int y) = 0;
|
||||||
|
|
||||||
|
// Get 24-bit RGB pixel color at specified location
|
||||||
|
// unsigned int will likely be 32-bit on 32-bit systems, and
|
||||||
|
// possible 64-bit on some 64-bit systems. In either case,
|
||||||
|
// it is large enough to hold a 16-bit color.
|
||||||
|
virtual unsigned int getPixel(int x, int y) = 0;
|
||||||
|
|
||||||
|
// This should reset entire context to the current background
|
||||||
|
virtual void clear()=0;
|
||||||
|
|
||||||
|
// These are the naive implementations that use setPixel,
|
||||||
|
// but are overridable should a context have a better-
|
||||||
|
// performing version available.
|
||||||
|
|
||||||
|
/* will need to be provided by the concrete
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* x0, y0 - origin of line
|
||||||
|
* x1, y1 - end of line
|
||||||
|
*
|
||||||
|
* Returns: void
|
||||||
|
*/
|
||||||
|
virtual void drawLine(int x0, int y0, int x1, int y1);
|
||||||
|
|
||||||
|
/* will need to be provided by the concrete
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* x0, y0 - origin/center of circle
|
||||||
|
* radius - radius of circle
|
||||||
|
*
|
||||||
|
* Returns: void
|
||||||
|
*/
|
||||||
|
virtual void drawCircle(int x0, int y0, unsigned int radius);
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* Event loop operations
|
||||||
|
*********************************************************/
|
||||||
|
|
||||||
|
// Run Event loop. This routine will receive events from
|
||||||
|
// the implementation and pass them along to the drawing. It
|
||||||
|
// will return when the window is closed or other implementation-
|
||||||
|
// specific sequence.
|
||||||
|
virtual void runLoop(DrawingBase* drawing) = 0;
|
||||||
|
|
||||||
|
// This method will end the current loop if one is running
|
||||||
|
// a default version is supplied
|
||||||
|
virtual void endLoop();
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* Utility operations
|
||||||
|
*********************************************************/
|
||||||
|
|
||||||
|
// returns the width of the window
|
||||||
|
virtual int getWindowWidth() = 0;
|
||||||
|
|
||||||
|
// returns the height of the window
|
||||||
|
virtual int getWindowHeight() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// this flag is used to control whether the event loop
|
||||||
|
// continues to run.
|
||||||
|
bool run;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
28
Lab4/main.cpp
Normal file
28
Lab4/main.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include "x11context.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
GraphicsContext* gc = new X11Context(800,600,GraphicsContext::BLACK);
|
||||||
|
|
||||||
|
|
||||||
|
// draw some lines
|
||||||
|
gc->setColor(GraphicsContext::GREEN);
|
||||||
|
gc->setPixel(10,10);
|
||||||
|
gc->setPixel(30,30);
|
||||||
|
gc->drawLine(100,100,500,500);
|
||||||
|
gc->setColor(GraphicsContext::RED);
|
||||||
|
gc->drawLine(100,500,500,500);
|
||||||
|
gc->setColor(GraphicsContext::BLUE);
|
||||||
|
gc->drawLine(500,500,500,100);
|
||||||
|
gc->setColor(GraphicsContext::YELLOW);
|
||||||
|
gc->drawLine(500,100,100,100);
|
||||||
|
gc->setColor(GraphicsContext::MAGENTA);
|
||||||
|
gc->drawCircle(300,300,200);
|
||||||
|
sleep(10);
|
||||||
|
|
||||||
|
delete gc;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
25
Lab4/makefile
Normal file
25
Lab4/makefile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# lab4 Makefile
|
||||||
|
|
||||||
|
CC = g++
|
||||||
|
CFLAGS = -c -MMD -g
|
||||||
|
LFLAGS = -lX11
|
||||||
|
# Change w/ every new project
|
||||||
|
SOURCES = main.cpp gcontext.cpp x11context.cpp
|
||||||
|
OBJECTS = $(SOURCES:.cpp=.o)
|
||||||
|
# Change w/ every new project
|
||||||
|
EXECUTABLE = Lab4
|
||||||
|
|
||||||
|
all: $(EXECUTABLE) $(SOURCES)
|
||||||
|
|
||||||
|
$(EXECUTABLE): $(OBJECTS)
|
||||||
|
$(CC) -o $@ $(OBJECTS) $(LFLAGS)
|
||||||
|
|
||||||
|
-include *.d
|
||||||
|
|
||||||
|
%.o:%.cpp
|
||||||
|
$(CC) $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(EXECUTABLE)
|
||||||
|
rm -f $(OBJECTS)
|
||||||
|
rm -f *.d
|
||||||
239
Lab4/x11context.cpp
Normal file
239
Lab4/x11context.cpp
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
/* Provides a simple drawing context for X11/XWindows
|
||||||
|
* You must have the X11 dev libraries installed. If missing,
|
||||||
|
* 'sudo apt-get install libx11-dev' should help.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <X11/Xlib.h> // Every Xlib program must include this
|
||||||
|
#include <X11/Xutil.h> // needed for XGetPixel
|
||||||
|
#include <X11/XKBlib.h> // needed for keyboard setup
|
||||||
|
#include "x11context.h"
|
||||||
|
#include "drawbase.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The only constructor provided. Allows size of window and background
|
||||||
|
* color be specified.
|
||||||
|
* */
|
||||||
|
X11Context::X11Context(unsigned int sizex=400,unsigned int sizey=400,
|
||||||
|
unsigned int bg_color=GraphicsContext::BLACK)
|
||||||
|
{
|
||||||
|
// Open the display
|
||||||
|
display = XOpenDisplay(NULL);
|
||||||
|
|
||||||
|
// Holding a key in gives repeated key_press commands but only
|
||||||
|
// one key_release
|
||||||
|
int supported;
|
||||||
|
|
||||||
|
XkbSetDetectableAutoRepeat(display,true,&supported);
|
||||||
|
|
||||||
|
// Create a window - we will assume the color map is in RGB mode.
|
||||||
|
window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0,
|
||||||
|
sizex, sizey, 0, 0 , bg_color);
|
||||||
|
|
||||||
|
// Sign up for MapNotify events
|
||||||
|
XSelectInput(display, window, StructureNotifyMask);
|
||||||
|
|
||||||
|
// Put the window on the screen
|
||||||
|
XMapWindow(display, window);
|
||||||
|
|
||||||
|
// Create a "Graphics Context"
|
||||||
|
graphics_context = XCreateGC(display, window, 0, NULL);
|
||||||
|
|
||||||
|
// Default color to white
|
||||||
|
XSetForeground(display, graphics_context, GraphicsContext::WHITE);
|
||||||
|
|
||||||
|
// Wait for MapNotify event
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
XEvent e;
|
||||||
|
XNextEvent(display, &e);
|
||||||
|
if (e.type == MapNotify)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We also want exposure, mouse, and keyboard events
|
||||||
|
XSelectInput(display, window, ExposureMask|
|
||||||
|
ButtonPressMask|
|
||||||
|
ButtonReleaseMask|
|
||||||
|
KeyPressMask|
|
||||||
|
KeyReleaseMask|
|
||||||
|
PointerMotionMask);
|
||||||
|
|
||||||
|
// We need this to get the WM_DELETE_WINDOW message from the
|
||||||
|
// window manager in case user click the X icon
|
||||||
|
Atom atomKill = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
||||||
|
XSetWMProtocols(display, window, &atomKill, 1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor - shut down window and connection to server
|
||||||
|
X11Context::~X11Context()
|
||||||
|
{
|
||||||
|
XFreeGC(display, graphics_context);
|
||||||
|
XDestroyWindow(display,window);
|
||||||
|
XCloseDisplay(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the drawing mode - argument is enumerated
|
||||||
|
void X11Context::setMode(drawMode newMode)
|
||||||
|
{
|
||||||
|
if (newMode == GraphicsContext::MODE_NORMAL)
|
||||||
|
{
|
||||||
|
XSetFunction(display,graphics_context,GXcopy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XSetFunction(display,graphics_context,GXxor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set drawing color - assume colormap is 24 bit RGB
|
||||||
|
void X11Context::setColor(unsigned int color)
|
||||||
|
{
|
||||||
|
// Go ahead and set color here - better performance than setting
|
||||||
|
// on every setPixel
|
||||||
|
XSetForeground(display, graphics_context, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a pixel in the current color
|
||||||
|
void X11Context::setPixel(int x, int y)
|
||||||
|
{
|
||||||
|
XDrawPoint(display, window, graphics_context, x, y);
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int X11Context::getPixel(int x, int y)
|
||||||
|
{
|
||||||
|
XImage *image;
|
||||||
|
image = XGetImage (display, window, x, y, 1, 1, AllPlanes, XYPixmap);
|
||||||
|
XColor color;
|
||||||
|
color.pixel = XGetPixel (image, 0, 0);
|
||||||
|
XFree (image);
|
||||||
|
XQueryColor (display, DefaultColormap(display, DefaultScreen (display)),
|
||||||
|
&color);
|
||||||
|
// I now have RGB values, but, they are 16 bits each, I only want 8-bits
|
||||||
|
// each since I want a 24-bit RGB color value
|
||||||
|
unsigned int pixcolor = color.red & 0xff00;
|
||||||
|
pixcolor |= (color.green >> 8);
|
||||||
|
pixcolor <<= 8;
|
||||||
|
pixcolor |= (color.blue >> 8);
|
||||||
|
return pixcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void X11Context::clear()
|
||||||
|
{
|
||||||
|
XClearWindow(display, window);
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Run event loop
|
||||||
|
void X11Context::runLoop(DrawingBase* drawing)
|
||||||
|
{
|
||||||
|
run = true;
|
||||||
|
|
||||||
|
while(run)
|
||||||
|
{
|
||||||
|
XEvent e;
|
||||||
|
XNextEvent(display, &e);
|
||||||
|
|
||||||
|
// Exposure event - lets not worry about region
|
||||||
|
if (e.type == Expose)
|
||||||
|
drawing->paint(this);
|
||||||
|
|
||||||
|
// Key Down
|
||||||
|
else if (e.type == KeyPress)
|
||||||
|
drawing->keyDown(this,XLookupKeysym((XKeyEvent*)&e,
|
||||||
|
(((e.xkey.state&0x01)&&!(e.xkey.state&0x02))||
|
||||||
|
(!(e.xkey.state&0x01)&&(e.xkey.state&0x02)))?1:0));
|
||||||
|
|
||||||
|
// Key Up
|
||||||
|
else if (e.type == KeyRelease){
|
||||||
|
drawing->keyUp(this,XLookupKeysym((XKeyEvent*)&e,
|
||||||
|
(((e.xkey.state&0x01)&&!(e.xkey.state&0x02))||
|
||||||
|
(!(e.xkey.state&0x01)&&(e.xkey.state&0x02)))?1:0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mouse Button Down
|
||||||
|
else if (e.type == ButtonPress)
|
||||||
|
drawing->mouseButtonDown(this,
|
||||||
|
e.xbutton.button,
|
||||||
|
e.xbutton.x,
|
||||||
|
e.xbutton.y);
|
||||||
|
|
||||||
|
// Mouse Button Up
|
||||||
|
else if (e.type == ButtonRelease)
|
||||||
|
drawing->mouseButtonUp(this,
|
||||||
|
e.xbutton.button,
|
||||||
|
e.xbutton.x,
|
||||||
|
e.xbutton.y);
|
||||||
|
|
||||||
|
// Mouse Move
|
||||||
|
else if (e.type == MotionNotify)
|
||||||
|
drawing->mouseMove(this,
|
||||||
|
e.xmotion.x,
|
||||||
|
e.xmotion.y);
|
||||||
|
|
||||||
|
// This will respond to the WM_DELETE_WINDOW from the
|
||||||
|
// window manager.
|
||||||
|
else if (e.type == ClientMessage)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int X11Context::getWindowWidth()
|
||||||
|
{
|
||||||
|
XWindowAttributes window_attributes;
|
||||||
|
XGetWindowAttributes(display,window, &window_attributes);
|
||||||
|
return window_attributes.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int X11Context::getWindowHeight()
|
||||||
|
{
|
||||||
|
XWindowAttributes window_attributes;
|
||||||
|
XGetWindowAttributes(display,window, &window_attributes);
|
||||||
|
return window_attributes.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// void X11Context::drawLine(int x1, int y1, int x2, int y2)
|
||||||
|
// {
|
||||||
|
// XDrawLine(display, window, graphics_context, x1, y1, x2, y2);
|
||||||
|
// XFlush(display);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Bresenham Implementation
|
||||||
|
void X11Context::drawLine(int x1, int y1, int x2, int y2){
|
||||||
|
//setPixel(x1,y1);
|
||||||
|
// Check direction of line
|
||||||
|
if(abs(y2-y1) < abs(x2-x1)){
|
||||||
|
if(x1 > x2){
|
||||||
|
//swap
|
||||||
|
} else {
|
||||||
|
//swap
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
int dx = x2-x1;
|
||||||
|
int dy = y2-y1;
|
||||||
|
int D = 2*dy-dx;
|
||||||
|
int y = y1;
|
||||||
|
for(int i = x1; i < x2; i++){
|
||||||
|
setPixel(i,y);
|
||||||
|
if(D > 0){
|
||||||
|
y = y+1;
|
||||||
|
D = D-2*dx;
|
||||||
|
}
|
||||||
|
D = D+2*dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void X11Context::drawCircle(int x, int y, unsigned int radius)
|
||||||
|
{
|
||||||
|
XDrawArc(display, window, graphics_context, x-radius,
|
||||||
|
y-radius, radius*2, radius*2, 0, 360*64);
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
49
Lab4/x11context.h
Normal file
49
Lab4/x11context.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef X11_CONTEXT
|
||||||
|
#define X11_CONTEXT
|
||||||
|
/**
|
||||||
|
* This class is a sample implementation of the GraphicsContext class
|
||||||
|
* for the X11 / XWindows system.
|
||||||
|
* */
|
||||||
|
|
||||||
|
#include <X11/Xlib.h> // Every Xlib program must include this
|
||||||
|
#include "gcontext.h" // base class
|
||||||
|
|
||||||
|
class X11Context : public GraphicsContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Default Constructor
|
||||||
|
X11Context(unsigned int sizex,unsigned int sizey,unsigned int bg_color);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
virtual ~X11Context();
|
||||||
|
|
||||||
|
// Drawing Operations
|
||||||
|
void setMode(drawMode newMode);
|
||||||
|
void setColor(unsigned int color);
|
||||||
|
void setPixel(int x, int y);
|
||||||
|
unsigned int getPixel(int x, int y);
|
||||||
|
void clear();
|
||||||
|
//void drawLine(int x1, int y1, int x2, int y2);
|
||||||
|
void drawLine(int x1, int y1, int x2, int y2);
|
||||||
|
void drawCircle(int x, int y, unsigned int radius);
|
||||||
|
|
||||||
|
|
||||||
|
// Event looop functions
|
||||||
|
void runLoop(DrawingBase* drawing);
|
||||||
|
|
||||||
|
// we will use endLoop provided by base class
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
int getWindowWidth();
|
||||||
|
int getWindowHeight();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
// X11 stuff - specific to this context
|
||||||
|
Display* display;
|
||||||
|
Window window;
|
||||||
|
GC graphics_context;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user