xorg: X11rdp

ulab-original
Jay Sorg 13 years ago
parent 746f14ffdd
commit 288961c60f

@ -6,9 +6,13 @@ LIBBASE = $(X11RDPBASE)/lib
XSRCBASE = ../build_dir/xorg-server-1.9.3
OBJS = rdpmain.o rdpdraw.o rdpinput.o rdpmisc.o rdpup.o miinitext.o fbcmap.o
OBJS = rdpmain.o rdpdraw.o rdpinput.o rdpmisc.o rdpup.o miinitext.o \
fbcmap_mi.o
# fbcmap.o
# in Xorg 7.1, fbcmap.c was used but now it looks like fbcmap_mi.c should
# be used
#fbcmap_mi.o
#fbcmap.o
LIBS = $(XSRCBASE)/dbe/.libs/libdbe.a \
$(XSRCBASE)/dix/.libs/libdix.a \
@ -25,7 +29,7 @@ LIBS = $(XSRCBASE)/dbe/.libs/libdbe.a \
$(XSRCBASE)/glx/.libs/libglx.a \
$(XSRCBASE)/xfixes/.libs/libxfixes.a \
librdp.a \
-lfreetype -lz -lm -lXfont -lXau -lXdmcp -lpixman-1 -lrt -ldl -lcrypto
-lfreetype -lz -lm -lXfont -lXau -lXdmcp -lpixman-1 -lrt -ldl -lcrypto -lGL
CFLAGS = -g -Wall -fno-strength-reduce \
-I../../include \
@ -44,7 +48,7 @@ CFLAGS = -g -Wall -fno-strength-reduce \
-I../xfree86/os-support \
-D_POSIX_C_SOURCE=199309L -D_SVID_SOURCE -D_REENTRANT \
-DGLX_USE_MESA -DXRECORD -D_GNU_SOURCE -DXAPPGROUP \
-DTOGCUP -DSINGLEDEPTH \
-DTOGCUP -DSINGLEDEPTH -DXFree86Server \
-DDBE -DEVI -DXVMC -DFONTCACHE -DGCCUSESGAS -DSTATIC_COLOR \
-DAVOID_GLYPHBLT -DFUNCPROTO=15 -DNARROWPROTO -DDDXOSFATALERROR \
-DPART_NET -DDDXTIME -D_HAVE_XALLOC_DECLS \
@ -57,7 +61,7 @@ CFLAGS = -g -Wall -fno-strength-reduce \
-DMITSHM -DPIXPRIV -DNDEBUG -DDDXOSINIT -DXKB -DXINPUT
# -pedantic
# -DXCSECURITY -DXF86BIGFONT -DXFree86Server
# -DXCSECURITY -DXF86BIGFONT
# these are defined in xorg-server.h
# -D_XOPEN_SOURCE=500L
@ -88,3 +92,6 @@ miinitext.o: ../build_dir/xorg-server-1.9.3/mi/miinitext.c
fbcmap.o: ../build_dir/xorg-server-1.9.3/fb/fbcmap.c
$(CC) $(CFLAGS) -c ../build_dir/xorg-server-1.9.3/fb/fbcmap.c
fbcmap_mi.o: ../build_dir/xorg-server-1.9.3/fb/fbcmap_mi.c
$(CC) $(CFLAGS) -c ../build_dir/xorg-server-1.9.3/fb/fbcmap_mi.c

@ -28,9 +28,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netdb.h>
@ -65,6 +67,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "fb.h"
#include "micmap.h"
#include "events.h"
#include "exevents.h"
#include "xserver-properties.h"
#include "xkbsrv.h"
//#include "colormapst.h"
/* test to see if this is xorg source or xfree86 */
#ifdef XORGSERVER
@ -111,6 +118,9 @@ struct _rdpScreenInfoRec
//PaintWindowBackgroundProcPtr PaintWindowBackground;
//PaintWindowBorderProcPtr PaintWindowBorder;
CreateColormapProcPtr CreateColormap;
DestroyColormapProcPtr DestroyColormap;
CopyWindowProcPtr CopyWindow;
ClearToBackgroundProcPtr ClearToBackground;
ScreenWakeupHandlerProcPtr WakeupHandler;
@ -178,6 +188,14 @@ int
g_tcp_local_bind(int sck, char* port);
int
g_tcp_listen(int sck);
int
g_create_dir(const char* dirname);
int
g_directory_exist(const char* dirname);
int
g_chmod_hex(const char* filename, int flags);
void
hexdump(unsigned char *p, unsigned int len);
/* rdpdraw.c */
Bool

@ -44,6 +44,8 @@ keyboard and mouse stuff
#define DEBUG_OUT_INPUT(arg) ErrorF arg
#endif
extern ScreenPtr g_pScreen; /* in rdpmain.c */
static DeviceIntPtr g_kbdDevice = 0;
static int g_old_button_mask = 0;
static int g_pause_spe = 0;
@ -55,6 +57,9 @@ static int g_tab_down = 0;
above *_down vars */
static int g_scroll_lock_down = 0;
static DeviceIntPtr g_mouse = 0;
static DeviceIntPtr g_keyboard = 0;
#define MIN_KEY_CODE 8
#define MAX_KEY_CODE 255
#define NO_OF_KEYS ((MAX_KEY_CODE - MIN_KEY_CODE) + 1)
@ -234,12 +239,14 @@ static KeySym g_kbdMap[] =
NoSymbol, NoSymbol
};
#if 0
/******************************************************************************/
static void
rdpSendBell(void)
{
DEBUG_OUT_INPUT(("rdpSendBell\n"));
}
#endif
/******************************************************************************/
void
@ -297,6 +304,20 @@ KbdDeviceOff(void)
DEBUG_OUT_INPUT(("KbdDeviceOff\n"));
}
/******************************************************************************/
void
rdpBell(int volume, DeviceIntPtr pDev, pointer ctrl, int cls)
{
ErrorF("rdpBell:\n");
}
/******************************************************************************/
void
rdpChangeKeyboardControl(DeviceIntPtr pDev, KeybdCtrl* ctrl)
{
ErrorF("rdpChangeKeyboardControl:\n");
}
/******************************************************************************/
int
rdpKeybdProc(DeviceIntPtr pDevice, int onoff)
@ -304,20 +325,29 @@ rdpKeybdProc(DeviceIntPtr pDevice, int onoff)
KeySymsRec keySyms;
CARD8 modMap[MAP_LENGTH];
DevicePtr pDev;
XkbRMLVOSet set[MAP_LENGTH];
XkbRMLVOSet set;
DEBUG_OUT_INPUT(("rdpKeybdProc\n"));
#if 1
pDev = (DevicePtr)pDevice;
switch (onoff)
{
case DEVICE_INIT:
KbdDeviceInit(pDevice, &keySyms, modMap);
InitKeyboardDeviceStruct(pDevice, set, (BellProcPtr)rdpSendBell,
(KbdCtrlProcPtr)NoopDDA);
memset(&set, 0, sizeof(set));
set.rules = "base";
set.model = "pc104";
set.layout = "us";
set.variant = "";
set.options = "";
InitKeyboardDeviceStruct(pDevice, &set, rdpBell,
rdpChangeKeyboardControl);
//XkbDDXChangeControls(pDevice, 0, 0);
break;
case DEVICE_ON:
pDev->on = 1;
KbdDeviceOn();
g_keyboard = pDevice;
break;
case DEVICE_OFF:
pDev->on = 0;
@ -330,6 +360,7 @@ rdpKeybdProc(DeviceIntPtr pDevice, int onoff)
}
break;
}
#endif
return Success;
}
@ -361,42 +392,54 @@ PtrDeviceOff(void)
DEBUG_OUT_INPUT(("PtrDeviceOff\n"));
}
/******************************************************************************/
static void
rdpMouseCtrl(DeviceIntPtr pDevice, PtrCtrl* pCtrl)
{
ErrorF("rdpMouseCtrl:\n");
}
/******************************************************************************/
int
rdpMouseProc(DeviceIntPtr pDevice, int onoff)
{
BYTE map[6];
DevicePtr pDev;
Atom btn_labels[6];
Atom axes_labels[2];
DEBUG_OUT_INPUT(("rdpMouseProc\n"));
pDev = (DevicePtr)pDevice;
switch (onoff)
{
case DEVICE_INIT:
PtrDeviceInit();
PtrDeviceInit();
map[0] = 0;
map[1] = 1;
map[2] = 2;
map[3] = 3;
map[4] = 4;
map[5] = 5;
//InitPointerDeviceStruct(pDevice, map, 5, 0, miPointerGetMotionEvents,
// PtrDeviceControl,
// miPointerGetMotionBufferSize(), 2, 0);
#if 0
DevicePtr /*device*/,
CARD8* /*map*/,
int /*numButtons*/,
Atom* /* btn_labels */,
PtrCtrlProcPtr /*controlProc*/,
int /*numMotionEvents*/,
int /*numAxes*/,
Atom* /* axes_labels */);
#endif
btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
InitPointerDeviceStruct(pDev, map, 5, btn_labels, rdpMouseCtrl,
GetMotionHistorySize(), 2, axes_labels);
break;
case DEVICE_ON:
pDev->on = 1;
PtrDeviceOn(pDevice);
g_mouse = pDevice;
break;
case DEVICE_OFF:
pDev->on = 0;
@ -635,41 +678,91 @@ rdpSpriteDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr)
ErrorF("rdpSpriteDeviceCursorCleanup:\n");
}
/******************************************************************************/
static void
rdpEnqueueMotion(int x, int y)
{
int i;
int n;
int valuators[2];
EventListPtr rdp_events;
xEvent* pev;
miPointerSetPosition(g_mouse, &x, &y);
valuators[0] = x;
valuators[1] = y;
GetEventList(&rdp_events);
n = GetPointerEvents(rdp_events, g_mouse, MotionNotify, 0,
POINTER_ABSOLUTE | POINTER_SCREEN,
0, 2, valuators);
for (i = 0; i < n; i++)
{
pev = (rdp_events + i)->event;
mieqEnqueue(g_mouse, (InternalEvent*)pev);
}
}
/******************************************************************************/
static void
rdpEnqueueButton(int type, int buttons)
{
int i;
int n;
EventListPtr rdp_events;
xEvent* pev;
i = GetEventList(&rdp_events);
n = GetPointerEvents(rdp_events, g_mouse, type, buttons, 0, 0, 0, 0);
for (i = 0; i < n; i++)
{
pev = (rdp_events + i)->event;
mieqEnqueue(g_mouse, (InternalEvent*)pev);
}
}
/******************************************************************************/
static void
rdpEnqueueKey(int type, int scancode)
{
int i;
int n;
EventListPtr rdp_events;
xEvent* pev;
i = GetEventList(&rdp_events);
n = GetKeyboardEvents(rdp_events, g_keyboard, type, scancode);
for (i = 0; i < n; i++)
{
pev = (rdp_events + i)->event;
mieqEnqueue(g_keyboard, (InternalEvent*)pev);
}
}
/******************************************************************************/
void
PtrAddEvent(int buttonMask, int x, int y)
{
//xEvent ev;
int i;
unsigned long time;
//InternalEvent e;
//memset(&e, 0, sizeof(e));
int type;
int buttons;
time = GetTimeInMillis();
//todo PostSyntheticMotion();
//miPointerAbsoluteCursor(x, y, time);
rdpEnqueueMotion(x, y);
for (i = 0; i < 5; i++)
{
if ((buttonMask ^ g_old_button_mask) & (1 << i))
{
if (buttonMask & (1 << i))
{
//e.header = ET_Internal;
//e.type = ET_ButtonPress;
//ev.u.u.type = ButtonPress;
//ev.u.u.detail = i + 1;
//ev.u.keyButtonPointer.time = time;
//mieqEnqueue(0, &ev); // todo
type = ButtonPress;
buttons = i + 1;
rdpEnqueueButton(type, buttons);
}
else
{
//ET_ButtonRelease
//ev.u.u.type = ButtonRelease;
//ev.u.u.detail = i + 1;
//ev.u.keyButtonPointer.time = time;
//mieqEnqueue(0, &ev); // todo
type = ButtonRelease;
buttons = i + 1;
rdpEnqueueButton(type, buttons);
}
}
}
@ -681,35 +774,19 @@ PtrAddEvent(int buttonMask, int x, int y)
void
check_keysa(void)
{
xEvent ev;
unsigned long time;
time = GetTimeInMillis();
if (g_ctrl_down != 0)
{
memset(&ev, 0, sizeof(ev));
ev.u.u.type = KeyRelease;
ev.u.keyButtonPointer.time = time;
ev.u.u.detail = g_ctrl_down;
//mieqEnqueue(&ev);
rdpEnqueueKey(KeyRelease, g_ctrl_down);
g_ctrl_down = 0;
}
if (g_alt_down != 0)
{
memset(&ev, 0, sizeof(ev));
ev.u.u.type = KeyRelease;
ev.u.keyButtonPointer.time = time;
ev.u.u.detail = g_alt_down;
//mieqEnqueue(&ev);
rdpEnqueueKey(KeyRelease, g_alt_down);
g_alt_down = 0;
}
if (g_shift_down != 0)
{
memset(&ev, 0, sizeof(ev));
ev.u.u.type = KeyRelease;
ev.u.keyButtonPointer.time = time;
ev.u.u.detail = g_shift_down;
//mieqEnqueue(&ev);
rdpEnqueueKey(KeyRelease, g_shift_down);
g_shift_down = 0;
}
}
@ -718,17 +795,13 @@ check_keysa(void)
void
KbdAddEvent(int down, int param1, int param2, int param3, int param4)
{
xEvent ev;
unsigned long time;
int rdp_scancode;
int x_scancode;
int is_ext;
int is_spe;
int type;
memset(&ev, 0, sizeof(ev));
ev.u.u.type = down ? KeyPress : KeyRelease;
time = GetTimeInMillis();
ev.u.keyButtonPointer.time = time;
type = down ? KeyPress : KeyRelease;
rdp_scancode = param3;
is_ext = param4 & 256; /* 0x100 */
is_spe = param4 & 512; /* 0x200 */
@ -851,8 +924,7 @@ KbdAddEvent(int down, int param1, int param2, int param3, int param4)
}
if (x_scancode > 0)
{
ev.u.u.detail = x_scancode;
//mieqEnqueue(&ev);
rdpEnqueueKey(type, x_scancode);
}
}
@ -864,32 +936,78 @@ KbdAddEvent(int down, int param1, int param2, int param3, int param4)
void
KbdSync(int param1)
{
#if 0
KeyClassPtr keyc;
int mask;
int latches;
int status;
if (g_kbdDevice == 0)
{
return;
}
mask = 0;
latches = 0;
mask |= 1 << 1;
if (param1 & 4)
{
latches |= 1 << 1;
}
mask |= Mod1Mask;
if (param1 & 4)
{
latches |= Mod1Mask;
}
ErrorF("mask 0x%x latches 0x%x\n", mask, latches);
status = XkbLatchModifiers(g_keyboard, mask, latches);
ErrorF("status %d\n", status);
#if 0
keyc = g_kbdDevice->key;
if (keyc == 0)
{
return;
}
#if 0
if ((!(keyc->state & 0x02)) != (!(param1 & 4))) /* caps lock */
ErrorF("0x%x mods 0x%x\n", param1, keyc->xkbInfo->state.mods);
hexdump(keyc->down, DOWN_LENGTH);
hexdump(keyc->modifierKeyCount, 32);
hexdump(keyc->xkbInfo, sizeof(struct _XkbSrvInfo));
#if 1
//if ((!(keyc->xkbInfo->state & 0x02)) != (!(param1 & 4))) /* caps lock */
if (param1 & 4) /* caps lock */
{
KbdAddEvent(1, 58, 0, 58, 0);
KbdAddEvent(0, 58, 49152, 58, 49152);
}
if ((!(keyc->state & 0x10)) != (!(param1 & 2))) /* num lock */
//if ((!(keyc->state & 0x10)) != (!(param1 & 2))) /* num lock */
if (param1 & 2) /* num lock */
{
KbdAddEvent(1, 69, 0, 69, 0);
KbdAddEvent(0, 69, 49152, 69, 49152);
}
#endif
if ((!(g_scroll_lock_down)) != (!(param1 & 1))) /* scroll lock */
{
KbdAddEvent(1, 70, 0, 70, 0);
KbdAddEvent(0, 70, 49152, 70, 49152);
}
#endif
#endif
}

@ -172,6 +172,7 @@ rdpWakeupHandler1(pointer blockData, int result, pointer pReadmask)
rdpup_check();
}
#if 0
/******************************************************************************/
static Bool
rdpDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScreen)
@ -186,6 +187,24 @@ rdpDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScreen)
{
ErrorF("rdpDeviceCursorCleanupProcPtr:\n");
}
#endif
#if 0
/******************************************************************************/
Bool
rdpCreateColormap(ColormapPtr pCmap)
{
ErrorF("rdpCreateColormap:\n");
return 1;
}
/******************************************************************************/
static void
rdpDestroyColormap(ColormapPtr pColormap)
{
ErrorF("rdpDestroyColormap:\n");
}
#endif
/******************************************************************************/
/* returns boolean, true if everything is ok */
@ -325,6 +344,10 @@ rdpScreenInit(int index, ScreenPtr pScreen, int argc, char** argv)
/* Backing store procedures */
g_rdpScreen.RestoreAreas = pScreen->RestoreAreas;
g_rdpScreen.WakeupHandler = pScreen->WakeupHandler;
g_rdpScreen.CreateColormap = pScreen->CreateColormap;
g_rdpScreen.DestroyColormap = pScreen->DestroyColormap;
ps = GetPictureScreenIfSet(pScreen);
if (ps)
{
@ -353,11 +376,18 @@ rdpScreenInit(int index, ScreenPtr pScreen, int argc, char** argv)
/* Backing store procedures */
pScreen->RestoreAreas = rdpRestoreAreas;
#if 0
pScreen->CreateColormap = rdpCreateColormap;
pScreen->DestroyColormap = rdpDestroyColormap;
#endif
miPointerInitialize(pScreen, &g_rdpSpritePointerFuncs,
&g_rdpPointerCursorFuncs, 1);
//pScreen->DeviceCursorInitialize = rdpDeviceCursorInitialize;
//pScreen->DeviceCursorCleanup = rdpDeviceCursorCleanup;
#if 0
pScreen->DeviceCursorInitialize = rdpDeviceCursorInitialize;
pScreen->DeviceCursorCleanup = rdpDeviceCursorCleanup;
#endif
vis_found = 0;
vis = g_pScreen->visuals + (g_pScreen->numVisuals - 1);
@ -374,17 +404,22 @@ rdpScreenInit(int index, ScreenPtr pScreen, int argc, char** argv)
rdpLog("rdpScreenInit: couldn't find root visual\n");
exit(1);
}
if (g_rdpScreen.bitsPerPixel == 1)
{
ret = fbCreateDefColormap(pScreen);
}
else
ret = 1;
if (ret)
{
ret = fbCreateDefColormap(pScreen);
if (!ret)
{
ErrorF("rdpScreenInit: fbCreateDefColormap failed\n");
}
}
if (ret)
{
ret = rdpup_init();
if (!ret)
{
ErrorF("rdpScreenInit: rdpup_init failed\n");
}
}
if (ret)
{
@ -551,6 +586,8 @@ InitInput(int argc, char** argv)
mieqInit(k, p);
#endif
mieqInit();
}
/******************************************************************************/
@ -559,6 +596,7 @@ ddxGiveUp(void)
{
char unixSocketName[64];
ErrorF("ddxGiveUp:\n");
g_free(g_rdpScreen.pfbMemory);
if (g_initOutputCalled)
{

@ -437,6 +437,96 @@ g_tcp_listen(int sck)
return listen(sck, 2);
}
/*****************************************************************************/
/* returns boolean */
int
g_create_dir(const char* dirname)
{
#if defined(_WIN32)
return CreateDirectoryA(dirname, 0); // test this
#else
return mkdir(dirname, (mode_t)-1) == 0;
#endif
}
/*****************************************************************************/
/* returns boolean, non zero if the directory exists */
int
g_directory_exist(const char* dirname)
{
#if defined(_WIN32)
return 0; // use GetFileAttributes and check return value
// is not -1 and FILE_ATTRIBUT_DIRECTORY bit is set
#else
struct stat st;
if (stat(dirname, &st) == 0)
{
return S_ISDIR(st.st_mode);
}
else
{
return 0;
}
#endif
}
/*****************************************************************************/
/* returns error */
int
g_chmod_hex(const char* filename, int flags)
{
int fl;
fl = 0;
fl |= (flags & 0x4000) ? S_ISUID : 0;
fl |= (flags & 0x2000) ? S_ISGID : 0;
fl |= (flags & 0x1000) ? S_ISVTX : 0;
fl |= (flags & 0x0400) ? S_IRUSR : 0;
fl |= (flags & 0x0200) ? S_IWUSR : 0;
fl |= (flags & 0x0100) ? S_IXUSR : 0;
fl |= (flags & 0x0040) ? S_IRGRP : 0;
fl |= (flags & 0x0020) ? S_IWGRP : 0;
fl |= (flags & 0x0010) ? S_IXGRP : 0;
fl |= (flags & 0x0004) ? S_IROTH : 0;
fl |= (flags & 0x0002) ? S_IWOTH : 0;
fl |= (flags & 0x0001) ? S_IXOTH : 0;
return chmod(filename, fl);
}
/* produce a hex dump */
void
hexdump(unsigned char* p, unsigned int len)
{
unsigned char* line;
int i;
int thisline;
int offset;
offset = 0;
line = p;
while (offset < len)
{
ErrorF("%04x ", offset);
thisline = len - offset;
if (thisline > 16)
thisline = 16;
for (i = 0; i < thisline; i++)
ErrorF("%02x ", line[i]);
for (; i < 16; i++)
ErrorF(" ");
for (i = 0; i < thisline; i++)
ErrorF("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
ErrorF("\n");
offset += thisline;
line += thisline;
}
}
/*
stub for XpClient* functions.
*/

@ -282,7 +282,7 @@ rdpup_recv_msg(struct stream* s)
static int
process_screen_size_msg(int width, int height, int bpp)
{
RRScreenSizePtr pSize;
//RRScreenSizePtr pSize;
int mmwidth;
int mmheight;
//int error;
@ -314,8 +314,8 @@ process_screen_size_msg(int width, int height, int bpp)
}
mmwidth = PixelToMM(width);
mmheight = PixelToMM(height);
pSize = RRRegisterSize(g_pScreen, width, height, mmwidth, mmheight);
RRSetCurrentConfig(g_pScreen, RR_Rotate_0, 0, pSize);
//pSize = RRRegisterSize(g_pScreen, width, height, mmwidth, mmheight);
//RRSetCurrentConfig(g_pScreen, RR_Rotate_0, 0, pSize);
if ((g_rdpScreen.width != width) || (g_rdpScreen.height != height))
{
//error = RRSetScreenConfig(g_pScreen, RR_Rotate_0, 0, pSize);
@ -445,6 +445,15 @@ rdpup_init(void)
char text[256];
int i;
if (!g_directory_exist("/tmp/.xrdp"))
{
if (!g_create_dir("/tmp/.xrdp"))
{
ErrorF("rdpup_init: g_create_dir failed\n");
return 0;
}
g_chmod_hex("/tmp/.xrdp", 0x1777);
}
i = atoi(display);
if (i < 1)
{

Loading…
Cancel
Save