Updated changes.txt
This commit is contained in:
parent
5fa6570f33
commit
632cd53f89
392
4ed_app_models.h
392
4ed_app_models.h
|
@ -1,196 +1,196 @@
|
||||||
/*
|
/*
|
||||||
* Mr. 4th Dimention - Allen Webster
|
* Mr. 4th Dimention - Allen Webster
|
||||||
*
|
*
|
||||||
* 06.05.2016 (dd.mm.yyyy)
|
* 06.05.2016 (dd.mm.yyyy)
|
||||||
*
|
*
|
||||||
* Global app level settings definition
|
* Global app level settings definition
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TOP
|
// TOP
|
||||||
|
|
||||||
#if !defined(FRED_APP_MODELS_H)
|
#if !defined(FRED_APP_MODELS_H)
|
||||||
#define FRED_APP_MODELS_H
|
#define FRED_APP_MODELS_H
|
||||||
|
|
||||||
struct App_Settings{
|
struct App_Settings{
|
||||||
char *init_files[8];
|
char *init_files[8];
|
||||||
i32 init_files_count;
|
i32 init_files_count;
|
||||||
i32 init_files_max;
|
i32 init_files_max;
|
||||||
|
|
||||||
char **custom_flags;
|
char **custom_flags;
|
||||||
i32 custom_flags_count;
|
i32 custom_flags_count;
|
||||||
|
|
||||||
i32 initial_line;
|
i32 initial_line;
|
||||||
b32 lctrl_lalt_is_altgr;
|
b32 lctrl_lalt_is_altgr;
|
||||||
|
|
||||||
i32 font_size;
|
i32 font_size;
|
||||||
b32 use_hinting;
|
b32 use_hinting;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum App_State{
|
enum App_State{
|
||||||
APP_STATE_EDIT,
|
APP_STATE_EDIT,
|
||||||
APP_STATE_RESIZING,
|
APP_STATE_RESIZING,
|
||||||
// never below this
|
// never below this
|
||||||
APP_STATE_COUNT
|
APP_STATE_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Model_View_Command_Function{
|
struct Model_View_Command_Function{
|
||||||
Model_View_Command_Function *next;
|
Model_View_Command_Function *next;
|
||||||
Custom_Command_Function *custom_func;
|
Custom_Command_Function *custom_func;
|
||||||
View_ID view_id;
|
View_ID view_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Model_Input_Event_Node{
|
struct Model_Input_Event_Node{
|
||||||
Model_Input_Event_Node *next;
|
Model_Input_Event_Node *next;
|
||||||
Input_Event event;
|
Input_Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Models{
|
struct Models{
|
||||||
Arena arena_;
|
Arena arena_;
|
||||||
Arena *arena;
|
Arena *arena;
|
||||||
Heap heap;
|
Heap heap;
|
||||||
|
|
||||||
App_Settings settings;
|
App_Settings settings;
|
||||||
App_State state;
|
App_State state;
|
||||||
|
|
||||||
Face_ID global_face_id;
|
Face_ID global_face_id;
|
||||||
|
|
||||||
Coroutine_Group coroutines;
|
Coroutine_Group coroutines;
|
||||||
|
|
||||||
Child_Process_Container child_processes;
|
Child_Process_Container child_processes;
|
||||||
Custom_API config_api;
|
Custom_API config_api;
|
||||||
|
|
||||||
Tick_Function *tick;
|
Tick_Function *tick;
|
||||||
Render_Caller_Function *render_caller;
|
Render_Caller_Function *render_caller;
|
||||||
Delta_Rule_Function *delta_rule;
|
Delta_Rule_Function *delta_rule;
|
||||||
u64 delta_rule_memory_size;
|
u64 delta_rule_memory_size;
|
||||||
|
|
||||||
Hook_Function *buffer_viewer_update;
|
Hook_Function *buffer_viewer_update;
|
||||||
Custom_Command_Function *view_event_handler;
|
Custom_Command_Function *view_event_handler;
|
||||||
Buffer_Name_Resolver_Function *buffer_name_resolver;
|
Buffer_Name_Resolver_Function *buffer_name_resolver;
|
||||||
Buffer_Hook_Function *begin_buffer;
|
Buffer_Hook_Function *begin_buffer;
|
||||||
Buffer_Hook_Function *end_buffer;
|
Buffer_Hook_Function *end_buffer;
|
||||||
Buffer_Hook_Function *new_file;
|
Buffer_Hook_Function *new_file;
|
||||||
Buffer_Hook_Function *save_file;
|
Buffer_Hook_Function *save_file;
|
||||||
Buffer_Edit_Range_Function *buffer_edit_range;
|
Buffer_Edit_Range_Function *buffer_edit_range;
|
||||||
Buffer_Region_Function *buffer_region;
|
Buffer_Region_Function *buffer_region;
|
||||||
Layout_Function *layout_func;
|
Layout_Function *layout_func;
|
||||||
|
|
||||||
Color_Table color_table_;
|
Color_Table color_table_;
|
||||||
|
|
||||||
Model_View_Command_Function *free_view_cmd_funcs;
|
Model_View_Command_Function *free_view_cmd_funcs;
|
||||||
Model_View_Command_Function *first_view_cmd_func;
|
Model_View_Command_Function *first_view_cmd_func;
|
||||||
Model_View_Command_Function *last_view_cmd_func;
|
Model_View_Command_Function *last_view_cmd_func;
|
||||||
|
|
||||||
Arena *virtual_event_arena;
|
Arena *virtual_event_arena;
|
||||||
Model_Input_Event_Node *free_virtual_event;
|
Model_Input_Event_Node *free_virtual_event;
|
||||||
Model_Input_Event_Node *first_virtual_event;
|
Model_Input_Event_Node *first_virtual_event;
|
||||||
Model_Input_Event_Node *last_virtual_event;
|
Model_Input_Event_Node *last_virtual_event;
|
||||||
|
|
||||||
Layout layout;
|
Layout layout;
|
||||||
Working_Set working_set;
|
Working_Set working_set;
|
||||||
Live_Views view_set;
|
Live_Views view_set;
|
||||||
Global_History global_history;
|
Global_History global_history;
|
||||||
Text_Layout_Container text_layouts;
|
Text_Layout_Container text_layouts;
|
||||||
Font_Set font_set;
|
Font_Set font_set;
|
||||||
|
|
||||||
Managed_ID_Set managed_id_set;
|
Managed_ID_Set managed_id_set;
|
||||||
Dynamic_Workspace dynamic_workspace;
|
Dynamic_Workspace dynamic_workspace;
|
||||||
Lifetime_Allocator lifetime_allocator;
|
Lifetime_Allocator lifetime_allocator;
|
||||||
|
|
||||||
Editing_File *message_buffer;
|
Editing_File *message_buffer;
|
||||||
Editing_File *scratch_buffer;
|
Editing_File *scratch_buffer;
|
||||||
Editing_File *log_buffer;
|
Editing_File *log_buffer;
|
||||||
Editing_File *keyboard_buffer;
|
Editing_File *keyboard_buffer;
|
||||||
|
|
||||||
Hot_Directory hot_directory;
|
Hot_Directory hot_directory;
|
||||||
|
|
||||||
b8 keep_playing;
|
b8 keep_playing;
|
||||||
b8 hard_exit;
|
b8 hard_exit;
|
||||||
|
|
||||||
b32 has_new_title;
|
b32 has_new_title;
|
||||||
char *title_space;
|
char *title_space;
|
||||||
i32 title_capacity;
|
i32 title_capacity;
|
||||||
|
|
||||||
Panel *resizing_intermediate_panel;
|
Panel *resizing_intermediate_panel;
|
||||||
|
|
||||||
Plat_Handle period_wakeup_timer;
|
Plat_Handle period_wakeup_timer;
|
||||||
i32 frame_counter;
|
i32 frame_counter;
|
||||||
u32 next_animate_delay;
|
u32 next_animate_delay;
|
||||||
b32 animate_next_frame;
|
b32 animate_next_frame;
|
||||||
|
|
||||||
Profile_Global_List profile_list;
|
Profile_Global_List profile_list;
|
||||||
|
|
||||||
// Last frame state
|
// Last frame state
|
||||||
Vec2_i32 prev_p;
|
Vec2_i32 prev_p;
|
||||||
Panel *prev_mouse_panel;
|
Panel *prev_mouse_panel;
|
||||||
b32 animated_last_frame;
|
b32 animated_last_frame;
|
||||||
u64 last_render_usecond_stamp;
|
u64 last_render_usecond_stamp;
|
||||||
|
|
||||||
// Event Context
|
// Event Context
|
||||||
Application_Step_Input *input;
|
Application_Step_Input *input;
|
||||||
i64 current_input_sequence_number;
|
i64 current_input_sequence_number;
|
||||||
User_Input current_input;
|
User_Input current_input;
|
||||||
b8 current_input_unhandled;
|
b8 current_input_unhandled;
|
||||||
|
|
||||||
b8 in_render_mode;
|
b8 in_render_mode;
|
||||||
Render_Target *target;
|
Render_Target *target;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
typedef i32 Dynamic_Workspace_Type;
|
typedef i32 Dynamic_Workspace_Type;
|
||||||
enum{
|
enum{
|
||||||
DynamicWorkspace_Global = 0,
|
DynamicWorkspace_Global = 0,
|
||||||
DynamicWorkspace_Unassociated = 1,
|
DynamicWorkspace_Unassociated = 1,
|
||||||
DynamicWorkspace_Buffer = 2,
|
DynamicWorkspace_Buffer = 2,
|
||||||
DynamicWorkspace_View = 3,
|
DynamicWorkspace_View = 3,
|
||||||
DynamicWorkspace_Intersected = 4,
|
DynamicWorkspace_Intersected = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Input_Types{
|
enum Input_Types{
|
||||||
Input_AnyKey,
|
Input_AnyKey,
|
||||||
Input_Esc,
|
Input_Esc,
|
||||||
Input_MouseMove,
|
Input_MouseMove,
|
||||||
Input_MouseLeftButton,
|
Input_MouseLeftButton,
|
||||||
Input_MouseRightButton,
|
Input_MouseRightButton,
|
||||||
Input_MouseWheel,
|
Input_MouseWheel,
|
||||||
Input_Count
|
Input_Count
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Consumption_Record{
|
struct Consumption_Record{
|
||||||
b32 consumed;
|
b32 consumed;
|
||||||
char consumer[32];
|
char consumer[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct File_Init{
|
struct File_Init{
|
||||||
String_Const_u8 name;
|
String_Const_u8 name;
|
||||||
Editing_File **ptr;
|
Editing_File **ptr;
|
||||||
b32 read_only;
|
b32 read_only;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Command_Line_Action{
|
enum Command_Line_Action{
|
||||||
CLAct_Nothing,
|
CLAct_Nothing,
|
||||||
CLAct_Ignore,
|
CLAct_Ignore,
|
||||||
CLAct_UserFile,
|
CLAct_UserFile,
|
||||||
CLAct_CustomDLL,
|
CLAct_CustomDLL,
|
||||||
CLAct_InitialFilePosition,
|
CLAct_InitialFilePosition,
|
||||||
CLAct_WindowSize,
|
CLAct_WindowSize,
|
||||||
CLAct_WindowMaximize,
|
CLAct_WindowMaximize,
|
||||||
CLAct_WindowPosition,
|
CLAct_WindowPosition,
|
||||||
CLAct_WindowFullscreen,
|
CLAct_WindowFullscreen,
|
||||||
CLAct_FontSize,
|
CLAct_FontSize,
|
||||||
CLAct_FontUseHinting,
|
CLAct_FontUseHinting,
|
||||||
//
|
//
|
||||||
CLAct_COUNT,
|
CLAct_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Command_Line_Mode{
|
enum Command_Line_Mode{
|
||||||
CLMode_App,
|
CLMode_App,
|
||||||
CLMode_Custom
|
CLMode_Custom
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// BOTTOM
|
// BOTTOM
|
||||||
|
|
||||||
|
|
|
@ -1,254 +0,0 @@
|
||||||
/*
|
|
||||||
* Mr. 4th Dimention - Allen Webster
|
|
||||||
*
|
|
||||||
* 20.08.2016
|
|
||||||
*
|
|
||||||
* File tracking shared.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TOP
|
|
||||||
|
|
||||||
struct File_Index{
|
|
||||||
u32 id[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef u32 rptr32;
|
|
||||||
|
|
||||||
#define to_ptr(b,p) ((void*)((char*)b + p))
|
|
||||||
#define to_rptr32(b,p) ((rptr32)((char*)(p) - (char*)(b)))
|
|
||||||
|
|
||||||
struct File_Track_Entry{
|
|
||||||
File_Index hash;
|
|
||||||
u32 opaque[4];
|
|
||||||
};
|
|
||||||
global_const File_Track_Entry null_file_track_entry = {};
|
|
||||||
|
|
||||||
struct File_Track_Tables{
|
|
||||||
i32 size;
|
|
||||||
u32 tracked_count;
|
|
||||||
u32 max;
|
|
||||||
rptr32 file_table;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DLL_Node {
|
|
||||||
DLL_Node *next;
|
|
||||||
DLL_Node *prev;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal File_Index
|
|
||||||
zero_file_index(){
|
|
||||||
File_Index a = {};
|
|
||||||
return(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal i32
|
|
||||||
file_hash_is_zero(File_Index a){
|
|
||||||
return ((a.id[0] == 0) &&
|
|
||||||
(a.id[1] == 0) &&
|
|
||||||
(a.id[2] == 0) &&
|
|
||||||
(a.id[3] == 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal i32
|
|
||||||
file_hash_is_deleted(File_Index a){
|
|
||||||
return ((a.id[0] == 0xFFFFFFFF) &&
|
|
||||||
(a.id[1] == 0xFFFFFFFF) &&
|
|
||||||
(a.id[2] == 0xFFFFFFFF) &&
|
|
||||||
(a.id[3] == 0xFFFFFFFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal i32
|
|
||||||
file_index_eq(File_Index a, File_Index b){
|
|
||||||
return ((a.id[0] == b.id[0]) &&
|
|
||||||
(a.id[1] == b.id[1]) &&
|
|
||||||
(a.id[2] == b.id[2]) &&
|
|
||||||
(a.id[3] == b.id[3]));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
|
||||||
insert_node(DLL_Node *pos, DLL_Node *node){
|
|
||||||
node->prev = pos;
|
|
||||||
node->next = pos->next;
|
|
||||||
pos->next = node;
|
|
||||||
node->next->prev = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
|
||||||
remove_node(DLL_Node *node){
|
|
||||||
node->next->prev = node->prev;
|
|
||||||
node->prev->next = node->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
|
||||||
init_sentinel_node(DLL_Node *node){
|
|
||||||
node->next = node;
|
|
||||||
node->prev = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal DLL_Node*
|
|
||||||
allocate_node(DLL_Node *sentinel){
|
|
||||||
DLL_Node *result = 0;
|
|
||||||
if (sentinel->next != sentinel){
|
|
||||||
result = sentinel->next;
|
|
||||||
remove_node(result);
|
|
||||||
}
|
|
||||||
return(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define FILE_ENTRY_COST (sizeof(File_Track_Entry))
|
|
||||||
|
|
||||||
|
|
||||||
internal i32
|
|
||||||
tracking_system_has_space(File_Track_Tables *tables, i32 new_count){
|
|
||||||
u32 count = tables->tracked_count;
|
|
||||||
u32 max = tables->max;
|
|
||||||
i32 result = ((count + new_count)*8 < max*7);
|
|
||||||
return(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal i32
|
|
||||||
entry_is_available(File_Track_Entry *entry){
|
|
||||||
i32 result = 0;
|
|
||||||
if (entry){
|
|
||||||
result =
|
|
||||||
file_hash_is_zero(entry->hash) ||
|
|
||||||
file_hash_is_deleted(entry->hash);
|
|
||||||
}
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal File_Track_Entry*
|
|
||||||
tracking_system_lookup_entry(File_Track_Tables *tables, File_Index key){
|
|
||||||
u32 hash = key.id[0];
|
|
||||||
u32 max = tables->max;
|
|
||||||
u32 index = (hash) % max;
|
|
||||||
u32 start = index;
|
|
||||||
|
|
||||||
File_Track_Entry *entries = (File_Track_Entry*)to_ptr(tables, tables->file_table);
|
|
||||||
|
|
||||||
File_Track_Entry* result = 0;
|
|
||||||
for (;;){
|
|
||||||
File_Track_Entry *entry = entries + index;
|
|
||||||
|
|
||||||
if (file_index_eq(entry->hash, key)){
|
|
||||||
result = entry;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (file_hash_is_zero(entry->hash)){
|
|
||||||
if (result == 0){
|
|
||||||
result = entry;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (file_hash_is_deleted(entry->hash)){
|
|
||||||
if (result == 0){
|
|
||||||
result = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++index;
|
|
||||||
if (index == max) index = 0;
|
|
||||||
if (index == start) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal File_Track_Entry*
|
|
||||||
get_file_entry(File_Track_Tables *tables, File_Index index){
|
|
||||||
File_Track_Entry *entry = 0;
|
|
||||||
|
|
||||||
File_Track_Entry *result = tracking_system_lookup_entry(tables, index);
|
|
||||||
if (result && file_index_eq(index, result->hash)){
|
|
||||||
entry = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
|
||||||
internal_free_slot(File_Track_Tables *tables, File_Track_Entry *entry){
|
|
||||||
Assert(!entry_is_available(entry));
|
|
||||||
|
|
||||||
*entry = null_file_track_entry;
|
|
||||||
entry->hash.id[0] = 0xFFFFFFFF;
|
|
||||||
entry->hash.id[1] = 0xFFFFFFFF;
|
|
||||||
entry->hash.id[2] = 0xFFFFFFFF;
|
|
||||||
entry->hash.id[3] = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
--tables->tracked_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal i32
|
|
||||||
enough_memory_to_init_table(i32 table_memory_size){
|
|
||||||
i32 result = (sizeof(File_Track_Tables) + FILE_ENTRY_COST*8 <= table_memory_size);
|
|
||||||
return(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
|
||||||
init_table_memory(File_Track_Tables *tables, i32 table_memory_size){
|
|
||||||
tables->size = table_memory_size;
|
|
||||||
tables->tracked_count = 0;
|
|
||||||
|
|
||||||
i32 max_number_of_entries = (table_memory_size - sizeof(*tables)) / FILE_ENTRY_COST;
|
|
||||||
|
|
||||||
tables->file_table = sizeof(*tables);
|
|
||||||
tables->max = max_number_of_entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal File_Track_Result
|
|
||||||
move_table_memory(File_Track_Tables *original_tables,
|
|
||||||
void *mem, i32 size){
|
|
||||||
File_Track_Result result = FileTrack_Good;
|
|
||||||
|
|
||||||
if (original_tables->size < size){
|
|
||||||
File_Track_Tables *tables = (File_Track_Tables*)mem;
|
|
||||||
|
|
||||||
// NOTE(allen): Initialize main data tables
|
|
||||||
{
|
|
||||||
tables->size = size;
|
|
||||||
|
|
||||||
i32 likely_entry_size = FILE_ENTRY_COST;
|
|
||||||
i32 max_number_of_entries = (size - sizeof(*tables)) / likely_entry_size;
|
|
||||||
|
|
||||||
tables->file_table = sizeof(*tables);
|
|
||||||
tables->max = max_number_of_entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tables->max > original_tables->max){
|
|
||||||
u32 original_max = original_tables->max;
|
|
||||||
|
|
||||||
// NOTE(allen): Rehash the tracking table
|
|
||||||
{
|
|
||||||
File_Track_Entry *entries = (File_Track_Entry*)
|
|
||||||
to_ptr(original_tables, original_tables->file_table);
|
|
||||||
|
|
||||||
for (u32 index = 0; index < original_max; ++index){
|
|
||||||
File_Track_Entry *entry = entries + index;
|
|
||||||
if (!entry_is_available(entry)){
|
|
||||||
File_Index hash = entry->hash;
|
|
||||||
File_Track_Entry *lookup =
|
|
||||||
tracking_system_lookup_entry(tables, hash);
|
|
||||||
|
|
||||||
Assert(entry_is_available(lookup));
|
|
||||||
*lookup = *entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tables->tracked_count = original_tables->tracked_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
result = FileTrack_MemoryTooSmall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
result = FileTrack_MemoryTooSmall;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// BOTTOM
|
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
+ Fix: the margin colors for panels are determined by the margins in theme files
|
+ Fix: the margin colors for panels are determined by the margins in theme files
|
||||||
+ Fix: when a file is deleted outside of 4coder, the '!' dirty status is added to the buffer
|
+ Fix: when a file is deleted outside of 4coder, the '!' dirty status is added to the buffer
|
||||||
+ Fix: on mac file changes outside of 4coder are detected and do not stall the UI
|
+ Fix: on mac file changes outside of 4coder are detected and do not stall the UI
|
||||||
|
+ Fix: in virtual whitespace layouts blank lines correctly mark carriage return characters before newline characters
|
||||||
|
+ Fix: auto-indentation leaves the carriage return in CRLF line endings in place and does not count them as indentation
|
||||||
|
+ Fix: lexer emit pointer advances correctly when the output buffer becomes full
|
||||||
|
+ Improvement: optimization in clean_all_lines command, handles CRLF line endings
|
||||||
|
|
||||||
4.1.2
|
4.1.2
|
||||||
+ Cursor color changes when recording macro if the theme provides a second cursor color
|
+ Cursor color changes when recording macro if the theme provides a second cursor color
|
||||||
|
|
Loading…
Reference in New Issue