- User input

- Input state polling & consuming
		- is_key_down, is_key_just_pressed, is_key_just_release
		- consume_key_down, consume_key_just_pressed, consume_key_just_released
	- Input events
		- Key event
		- Scroll event
		- Text event
- unicode.c
	- utf16_to_utf32
This commit is contained in:
Charlie 2024-06-29 17:54:30 +02:00
parent c61f216d37
commit 8a0fc81576
13 changed files with 501 additions and 19 deletions

View file

@ -4,6 +4,6 @@ mkdir build
pushd build
cl ../build.c /Zi /Fecgame.exe /Od /DDEBUG /MD /DEBUG /std:c11
cl.exe /Fe:cgame.exe ..\build.c /Od /std:c11 /W4 /wd4273 /wd4018 /wd4100 /Zi gdi32.lib user32.lib opengl32.lib
popd

View file

@ -2,9 +2,11 @@
///
// Build config stuff
#define VERY_DEBUG 0
#define RUN_TESTS 0
// When we need very debug
// #define CONFIGURATION VERY_DEBUG
typedef struct Context_Extra {
int monkee;
} Context_Extra;

View file

@ -4,6 +4,6 @@ mkdir build
pushd build
clang -g -o cgame.exe ../build.c -O0 -DDEBUG -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32
clang -g -o cgame.exe ../build.c -O0 -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32
popd

View file

@ -7,7 +7,7 @@ pushd build
mkdir release
pushd release
cl ../../build.c /Zi /Fecgame.exe /Ox /DRELEASE /MD /std:c11
cl.exe /Fe:cgame.exe ..\..\build.c /Ox /std:c11 /W4 /wd4273 /wd4018 /wd4100 gdi32.lib user32.lib opengl32.lib
popd
popd

View file

@ -7,7 +7,7 @@ pushd build
mkdir release
pushd release
clang -o cgame.exe ../../build.c -Ofast -DRELEASE -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32
clang -o cgame.exe ../../build.c -Ofast -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32
popd
popd

23
entry.c
View file

@ -15,15 +15,30 @@ int start(int argc, char **argv) {
reset_temporary_storage();
context.allocator = temp;
os_update();
os_update();
float64 now = os_get_current_time_in_seconds();
float64 delta = now - last_time;
last_time = now;
if (is_key_just_released(KEY_ESCAPE)) {
window.should_close = true;
}
if (is_key_just_released('E')) {
print("Mouse pos: %f, %f\n", input_frame.mouse_x, input_frame.mouse_y);
}
for (u64 i = 0; i < input_frame.number_of_events; i++) {
Input_Event e = input_frame.events[i];
// Print some random FPS samples every now and then
if ((get_random() % 4000) == 3)
print("%2.f FPS\n", 1.0/delta);
switch (e.kind) {
case INPUT_EVENT_KEY: print("Key event key code %d\n", e.key_code); break;
case INPUT_EVENT_SCROLL: print("Scroll event, x: %.3f, y: %.3f\n", e.xscroll, e.yscroll); break;
case INPUT_EVENT_TEXT: print("Text event, utf32: %d, ascii: %c\n", e.utf32, e.ascii); break;
}
}
draw_rect_rotated(v2(-.25f, -.25f), v2(.5f, .5f), COLOR_RED, v2(.25, .25), (f32)now);

View file

@ -21,6 +21,8 @@ typedef u8 bool;
#define thread_local _Thread_local
#define local_persist static
#define ifnt(x) if (!(x))
#ifdef _MSC_VER
@ -40,17 +42,20 @@ void printf(const char* fmt, ...);
#define assert_line(line, cond, ...) if (!(cond)) { printf("Assertion failed in file " __FILE__ " on line " STR(line) "\nFailed Condition: " #cond ". Message: " __VA_ARGS__); os_break(); }
#define assert(cond, ...) assert_line(__LINE__, cond, __VA_ARGS__);
#ifdef RELEASE
#if CONFIGRATION == RELEASE
#undef assert
#define assert(...)
#endif
#define panic(...) { print(__VA_ARGS__); os_break(); }
#define cast(t) (t)
#define ZERO(t) (t){0}
///
// Compiler specific stuff
// We make inline actually inline.
#ifdef _MSC_VER
// Microsoft Visual C++
#define inline __forceinline

View file

@ -0,0 +1,177 @@
/*
Main input juice:
bool is_key_down(Input_Key_Code code);
bool is_key_up(Input_Key_Code code);
bool is_key_just_pressed(Input_Key_Code code);
bool is_key_just_released(Input_Key_Code code);
bool consume_key_down(Input_Key_Code code);
bool consume_key_just_pressed(Input_Key_Code code);
bool consume_key_just_released(Input_Key_Code code);
// To loop through events this frame
for (u64 i = 0; i < input_frame.number_of_events; i++) {
Input_Event e = input_frame.events[i];
switch (e.kind) {
case INPUT_EVENT_KEY: ...; break;
case INPUT_EVENT_SCROLL: ...; break;
case INPUT_EVENT_TEXT: ...; break;
case INPUT_EVENT_CLOSE: ...; break;
}
}
*/
typedef enum Input_Event_Kind {
INPUT_EVENT_KEY,
INPUT_EVENT_SCROLL,
INPUT_EVENT_TEXT,
} Input_Event_Kind;
typedef enum Input_Key_Code {
KEY_UNKNOWN = 0,
// Non-textual keys that have placements in the ASCII table
// (and thus in Unicode):
KEY_BACKSPACE = 8,
KEY_TAB = 9,
KEY_ENTER = 13,
KEY_ESCAPE = 27,
KEY_SPACEBAR = 32,
// The letters A-Z live in here as well and may be returned
// by keyboard events.
KEY_DELETE = 127,
KEY_ARROW_UP = 128,
KEY_ARROW_DOWN = 129,
KEY_ARROW_LEFT = 130,
KEY_ARROW_RIGHT = 131,
KEY_PAGE_UP = 132,
KEY_PAGE_DOWN = 133,
KEY_HOME = 134,
KEY_END = 135,
KEY_INSERT = 136,
KEY_PAUSE = 137,
KEY_SCROLL_LOCK = 138,
KEY_ALT,
KEY_CTRL,
KEY_SHIFT,
KEY_CMD,
KEY_META = KEY_CMD,
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6,
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_F11,
KEY_F12,
KEY_PRINT_SCREEN,
MOUSE_BUTTON_LEFT,
MOUSE_BUTTON_MIDDLE,
MOUSE_BUTTON_RIGHT,
MOUSE_FIRST = MOUSE_BUTTON_LEFT,
MOUSE_LAST = MOUSE_BUTTON_RIGHT,
INPUT_KEY_CODE_COUNT
} Input_Key_Code;
typedef enum Input_State_Flags {
INPUT_STATE_DOWN = 1<<0,
INPUT_STATE_JUST_PRESSED = 1<<1,
INPUT_STATE_JUST_RELEASED = 1<<2,
INPUT_STATE_REPEAT = 1<<3,
} Input_State_Flags;
typedef struct Input_Event {
Input_Event_Kind kind;
// For INPUT_EVENT_KEY
Input_Key_Code key_code;
Input_State_Flags key_state;
// For INPUT_EVENT_SCROLL
float64 xscroll;
float64 yscroll;
// For INPUT_EVENT_TEXT_INPUT
union { u32 utf32; char ascii; };
} Input_Event;
Input_Key_Code os_key_to_key_code(void* os_key);
void* key_code_to_os_key(Input_Key_Code key_code);
#define MAX_EVENTS_PER_FRAME 10000
typedef struct Input_Frame {
Input_Event events[MAX_EVENTS_PER_FRAME];
u64 number_of_events;
float32 mouse_x;
float32 mouse_y;
Input_State_Flags key_states[INPUT_KEY_CODE_COUNT];
} Input_Frame;
Input_Frame input_frame = ZERO(Input_Frame);
bool has_key_state(Input_Key_Code code, Input_State_Flags flags) {
assert(code > 0 && code < INPUT_KEY_CODE_COUNT, "Invalid key code %d!", code);
Input_State_Flags state = input_frame.key_states[code];
#if CONFIGURATION == DEBUG
{
Input_State_Flags impossible = (INPUT_STATE_JUST_RELEASED | INPUT_STATE_DOWN);
assert((impossible & state) != impossible, "Key state for key '%d' is corrupt!", code);
impossible = (INPUT_STATE_JUST_RELEASED | INPUT_STATE_JUST_PRESSED);
assert((impossible & state) != impossible, "Key state for key '%d' is corrupt!", code);
}
#endif
return (state & flags) == flags;
}
bool is_key_down(Input_Key_Code code) {
return has_key_state(code, INPUT_STATE_DOWN);
}
bool is_key_up(Input_Key_Code code) {
return input_frame.key_states[code] == 0 || has_key_state(code, INPUT_STATE_JUST_RELEASED);
}
bool is_key_just_pressed(Input_Key_Code code) {
return has_key_state(code, INPUT_STATE_JUST_PRESSED);
}
bool is_key_just_released(Input_Key_Code code) {
return has_key_state(code, INPUT_STATE_JUST_RELEASED);
}
bool consume_key_down(Input_Key_Code code) {
bool result = is_key_down(code);
input_frame.key_states[code] &= ~(INPUT_STATE_DOWN);
return result;
}
bool consume_key_just_pressed(Input_Key_Code code) {
bool result = is_key_just_pressed(code);
input_frame.key_states[code] &= ~(INPUT_STATE_JUST_PRESSED);
return result;
}
bool consume_key_just_released(Input_Key_Code code) {
bool result = is_key_just_released(code);
input_frame.key_states[code] &= ~(INPUT_STATE_JUST_RELEASED);
return result;
}

View file

@ -25,12 +25,16 @@ void lodepng_free(void* ptr) {
/////
#if !defined(DEBUG) && !defined(RELEASE)
#define DEBUG 0
#define VERY_DEBUG 1
#define RELEASE 2
#if !defined(CONFIGURATION)
#ifdef _DEBUG
#define DEBUG
#define CONFIGURATION DEBUG
#elif defined(NDEBUG)
#define RELEASE
#define CONFIGURATION RELEASE
#endif
#endif
@ -76,6 +80,7 @@ void lodepng_free(void* ptr) {
#include "string.c"
#include "unicode.c"
#include "string_format.c"
#include "os_interface.c"

View file

@ -5,8 +5,49 @@
void* heap_alloc(u64);
void heap_dealloc(void*);
// Persistent
Input_State_Flags win32_key_states[INPUT_KEY_CODE_COUNT];
void win32_send_key_event(Input_Key_Code code, Input_State_Flags state) {
Input_Event e;
e.kind = INPUT_EVENT_KEY;
e.key_code = code;
e.key_state = state;
input_frame.events[input_frame.number_of_events] = e;
input_frame.number_of_events += 1;
}
void win32_handle_key_up(Input_Key_Code code) {
if (code == KEY_UNKNOWN) return;
Input_State_Flags last_state = win32_key_states[code];
Input_State_Flags state = 0;
if (last_state & INPUT_STATE_DOWN) state |= INPUT_STATE_JUST_RELEASED;
win32_key_states[code] = state;
win32_send_key_event(code, state);
}
void win32_handle_key_down(Input_Key_Code code) {
if (code == KEY_UNKNOWN) return;
Input_State_Flags last_state = win32_key_states[code];
Input_State_Flags state = INPUT_STATE_DOWN;
if (!(last_state & INPUT_STATE_DOWN)) state |= INPUT_STATE_JUST_PRESSED;
win32_key_states[code] = state;
win32_send_key_event(code, state);
}
void win32_handle_key_repeat(Input_Key_Code code) {
if (code == KEY_UNKNOWN) return;
win32_key_states[code] |= INPUT_STATE_REPEAT;
win32_send_key_event(code, win32_key_states[code]);
}
LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wparam, LPARAM lparam) {
if (window._initialized) {
@ -15,12 +56,76 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar
switch (message) {
case WM_CLOSE:
window.should_close = true;
DestroyWindow(window._os_handle);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
bool is_repeat = (lparam & 0x40000000) != 0;
if (is_repeat) win32_handle_key_repeat(os_key_to_key_code((void*)wparam));
else win32_handle_key_down (os_key_to_key_code((void*)wparam));
goto DEFAULT_HANDLE;
case WM_KEYUP:
win32_handle_key_up(os_key_to_key_code((void*)wparam));
goto DEFAULT_HANDLE;
case WM_LBUTTONDOWN:
win32_handle_key_down(MOUSE_BUTTON_LEFT);
goto DEFAULT_HANDLE;
case WM_RBUTTONDOWN:
win32_handle_key_down(MOUSE_BUTTON_RIGHT);
goto DEFAULT_HANDLE;
case WM_MBUTTONDOWN:
win32_handle_key_down(MOUSE_BUTTON_MIDDLE);
goto DEFAULT_HANDLE;
case WM_LBUTTONUP:
win32_handle_key_up(MOUSE_BUTTON_LEFT);
goto DEFAULT_HANDLE;
case WM_RBUTTONUP:
win32_handle_key_up(MOUSE_BUTTON_RIGHT);
goto DEFAULT_HANDLE;
case WM_MBUTTONUP:
win32_handle_key_up(MOUSE_BUTTON_MIDDLE);
goto DEFAULT_HANDLE;
case WM_MOUSEWHEEL: {
int delta = GET_WHEEL_DELTA_WPARAM(wparam);
int ticks = delta / WHEEL_DELTA;
Input_Event e;
e.kind = INPUT_EVENT_SCROLL;
e.yscroll = (float64)delta/(float64)WHEEL_DELTA;
e.xscroll = 0;
input_frame.events[input_frame.number_of_events] = e;
input_frame.number_of_events += 1;
goto DEFAULT_HANDLE;
}
case WM_MOUSEHWHEEL: {
int delta = GET_WHEEL_DELTA_WPARAM(wparam);
Input_Event e;
e.kind = INPUT_EVENT_SCROLL;
e.yscroll = 0;
e.xscroll = (float64)delta/(float64)WHEEL_DELTA;
input_frame.events[input_frame.number_of_events] = e;
input_frame.number_of_events += 1;
goto DEFAULT_HANDLE;
}
case WM_CHAR: {
wchar_t utf16 = (wchar_t)wparam;
Input_Event e;
e.kind = INPUT_EVENT_TEXT;
utf16_to_utf32(&utf16, 1, &e.utf32);
input_frame.events[input_frame.number_of_events] = e;
input_frame.number_of_events += 1;
goto DEFAULT_HANDLE;
}
default:
DEFAULT_HANDLE:
return DefWindowProc(passed_window, message, wparam, lparam);
}
return 0;
@ -346,7 +451,7 @@ bool os_compare_and_swap_bool(bool *a, bool b, bool old) {
void os_update() {
static Os_Window last_window;
local_persist Os_Window last_window;
if (!strings_match(last_window.title, window.title)) {
SetWindowText(window._os_handle, temp_convert_to_null_terminated_string(window.title));
@ -400,9 +505,20 @@ void os_update() {
last_window = window;
// Reflect what the backend did to input state before we query for OS inputs
memcpy(win32_key_states, input_frame.key_states, sizeof(input_frame.key_states));
input_frame.number_of_events = 0;
for (u64 i = 0; i < INPUT_KEY_CODE_COUNT; i++) {
win32_key_states[i] &= ~(INPUT_STATE_REPEAT);
win32_key_states[i] &= ~(INPUT_STATE_JUST_PRESSED);
win32_key_states[i] &= ~(INPUT_STATE_JUST_RELEASED);
}
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
while (input_frame.number_of_events < MAX_EVENTS_PER_FRAME
&& PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
window.should_close = true;
break;
@ -410,4 +526,128 @@ void os_update() {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
memcpy(input_frame.key_states, win32_key_states, sizeof(input_frame.key_states));
POINT p;
GetCursorPos(&p);
ScreenToClient(window._os_handle, &p);
p.y = window.height - p.y;
input_frame.mouse_x = (float32)p.x;
input_frame.mouse_y = (float32)p.y;
if (window.should_close) {
win32_window_proc(window._os_handle, WM_CLOSE, 0, 0);
}
}
Input_Key_Code os_key_to_key_code(void* os_key) {
UINT win32_key = (UINT)(u64)os_key;
if (win32_key >= 'A' && win32_key <= 'Z') {
return (Input_Key_Code)win32_key;
}
if (win32_key >= '0' && win32_key <= '9') {
return (Input_Key_Code)win32_key;
}
switch (win32_key) {
case VK_BACK: return KEY_BACKSPACE;
case VK_TAB: return KEY_TAB;
case VK_RETURN: return KEY_ENTER;
case VK_ESCAPE: return KEY_ESCAPE;
case VK_SPACE: return KEY_SPACEBAR;
case VK_DELETE: return KEY_DELETE;
case VK_UP: return KEY_ARROW_UP;
case VK_DOWN: return KEY_ARROW_DOWN;
case VK_LEFT: return KEY_ARROW_LEFT;
case VK_RIGHT: return KEY_ARROW_RIGHT;
case VK_PRIOR: return KEY_PAGE_UP;
case VK_NEXT: return KEY_PAGE_DOWN;
case VK_HOME: return KEY_HOME;
case VK_END: return KEY_END;
case VK_INSERT: return KEY_INSERT;
case VK_PAUSE: return KEY_PAUSE;
case VK_SCROLL: return KEY_SCROLL_LOCK;
case VK_MENU: return KEY_ALT;
case VK_CONTROL: return KEY_CTRL;
case VK_SHIFT: return KEY_SHIFT;
case VK_LWIN: return KEY_CMD;
case VK_RWIN: return KEY_CMD;
case VK_F1: return KEY_F1;
case VK_F2: return KEY_F2;
case VK_F3: return KEY_F3;
case VK_F4: return KEY_F4;
case VK_F5: return KEY_F5;
case VK_F6: return KEY_F6;
case VK_F7: return KEY_F7;
case VK_F8: return KEY_F8;
case VK_F9: return KEY_F9;
case VK_F10: return KEY_F10;
case VK_F11: return KEY_F11;
case VK_F12: return KEY_F12;
case VK_SNAPSHOT: return KEY_PRINT_SCREEN;
case VK_LBUTTON: return MOUSE_BUTTON_LEFT;
case VK_MBUTTON: return MOUSE_BUTTON_MIDDLE;
case VK_RBUTTON: return MOUSE_BUTTON_RIGHT;
default: return KEY_UNKNOWN;
}
}
void* key_code_to_os_key(Input_Key_Code key_code) {
if (key_code >= 'A' && key_code <= 'Z') {
return (void*)key_code;
}
if (key_code >= '0' && key_code <= '9') {
return (void*)key_code;
}
switch (key_code) {
case KEY_BACKSPACE: return (void*)VK_BACK;
case KEY_TAB: return (void*)VK_TAB;
case KEY_ENTER: return (void*)VK_RETURN;
case KEY_ESCAPE: return (void*)VK_ESCAPE;
case KEY_SPACEBAR: return (void*)VK_SPACE;
case KEY_DELETE: return (void*)VK_DELETE;
case KEY_ARROW_UP: return (void*)VK_UP;
case KEY_ARROW_DOWN: return (void*)VK_DOWN;
case KEY_ARROW_LEFT: return (void*)VK_LEFT;
case KEY_ARROW_RIGHT: return (void*)VK_RIGHT;
case KEY_PAGE_UP: return (void*)VK_PRIOR;
case KEY_PAGE_DOWN: return (void*)VK_NEXT;
case KEY_HOME: return (void*)VK_HOME;
case KEY_END: return (void*)VK_END;
case KEY_INSERT: return (void*)VK_INSERT;
case KEY_PAUSE: return (void*)VK_PAUSE;
case KEY_SCROLL_LOCK: return (void*)VK_SCROLL;
case KEY_ALT: return (void*)VK_MENU;
case KEY_CTRL: return (void*)VK_CONTROL;
case KEY_SHIFT: return (void*)VK_SHIFT;
case KEY_CMD: return (void*)VK_LWIN; // Assuming left win key for both
case KEY_F1: return (void*)VK_F1;
case KEY_F2: return (void*)VK_F2;
case KEY_F3: return (void*)VK_F3;
case KEY_F4: return (void*)VK_F4;
case KEY_F5: return (void*)VK_F5;
case KEY_F6: return (void*)VK_F6;
case KEY_F7: return (void*)VK_F7;
case KEY_F8: return (void*)VK_F8;
case KEY_F9: return (void*)VK_F9;
case KEY_F10: return (void*)VK_F10;
case KEY_F11: return (void*)VK_F11;
case KEY_F12: return (void*)VK_F12;
case KEY_PRINT_SCREEN: return (void*)VK_SNAPSHOT;
case MOUSE_BUTTON_LEFT: return (void*)VK_LBUTTON;
case MOUSE_BUTTON_MIDDLE: return (void*)VK_MBUTTON;
case MOUSE_BUTTON_RIGHT: return (void*)VK_RBUTTON;
case INPUT_KEY_CODE_COUNT:
case KEY_UNKNOWN:
break;
}
panic("Invalid key code %d", key_code);
return 0;
}

View file

@ -58,7 +58,7 @@ inline int crt_vprintf(const char* fmt, va_list args) {
return os.crt_vprintf(fmt, args);
}
#if !defined(COMPILER_HAS_MEMCPY_INTRINSICS) || defined(DEBUG)
#if !defined(COMPILER_HAS_MEMCPY_INTRINSICS) || CONFIGURATION == DEBUG
inline void* naive_memcpy(void* dest, const void* source, size_t size) {
for (u64 i = 0; i < (u64)size; i++) ((u8*)dest)[i] = ((u8*)source)[i];
return dest;
@ -139,7 +139,7 @@ void os_lock_mutex(Mutex_Handle m);
void os_unlock_mutex(Mutex_Handle m);
///
// Spinlock primitive
// Spinlock "primitive"
typedef struct Spinlock {
bool locked;
} Spinlock;

View file

@ -11,7 +11,7 @@ Usage:
string balls = const_string("balls");
// tprint for temporary allocation
string b = tprint(const_string("Hello, %s!\n"), balls); // %s for string
string b = tprint("Hello, %s!\n", balls); // %s for string
// Allocate a new string of length 12 (with context allocator)
string c = alloc_string(12);

38
oogabooga/unicode.c Normal file
View file

@ -0,0 +1,38 @@
#define UTF16_SURROGATE_HIGH_START 0xD800
#define UTF16_SURROGATE_HIGH_END 0xDBFF
#define UTF16_SURROGATE_LOW_START 0xDC00
#define UTF16_SURROGATE_LOW_END 0xDFFF
#define UTF16_SURROGATE_OFFSET 0x10000
#define UTF16_SURROGATE_MASK 0x3FF
// Returns how many utf16 units were converted
int utf16_to_utf32(const u16 *utf16, u64 length, u32 *utf32) {
if (length == 0 || utf16 == NULL || utf32 == NULL) {
return -1;
}
u16 first = utf16[0];
if (first >= UTF16_SURROGATE_HIGH_START && first <= UTF16_SURROGATE_HIGH_END) {
if (length < 2) {
return -1;
}
u16 second = utf16[1];
if (second >= UTF16_SURROGATE_LOW_START && second <= UTF16_SURROGATE_LOW_END) {
*utf32 = ((first - UTF16_SURROGATE_HIGH_START) << 10)
+ (second - UTF16_SURROGATE_LOW_START)
+ UTF16_SURROGATE_OFFSET;
return 2;
} else {
return -1;
}
} else if (first >= UTF16_SURROGATE_LOW_START && first <= UTF16_SURROGATE_LOW_END) {
return -1;
} else {
*utf32 = first;
return 1;
}
}