{
 * Keyman is copyright (C) SIL International. MIT License.
 *
 * Debug interface for Keyman Core, matches keyboardprocessor_debug.h
}
unit Keyman.System.KeymanCoreDebug;

interface

uses
  Keyman.System.KeymanCore;

{$WARN SYMBOL_PLATFORM OFF}
{$ALIGN 8}

///
/// The maximum size of context in km_kbp_cp units for a single debug
/// event. This is taken from MAXCONTEXT in keyman32 (Windows) and is purely
/// a convenience value. We can increase it if there is a demonstrated need.
///
const DEBUG_MAX_CONTEXT = 80;

///
/// The number of stores that can be processed in a rule. This is taken from
/// MAXSTOREOFFSETS in keyman32 (Windows) and is purely a convenience value.
/// We can increase it if there is a demonstrated need.
///
const DEBUG_MAX_STORE_OFFSETS = 20;
const DEBUG_STORE_OFFSETS_SIZE = (DEBUG_MAX_STORE_OFFSETS*2+1);

///
/// These modifier flags are used internally in the kmx engine, so will be
/// exposed only in debugging modifier states.
///
const KM_KBP_MODIFIER_VIRTUALKEY      = $4000;
const KM_KBP_MODIFIER_VIRTUALCHARKEY  = $8000;

///
/// Input key event data. The `character` member is derived from
/// a US English key event for vk + modifier_state, and is 0 if
/// the vk + modifier_state do not generate a character.
///
/// Used only in event type KM_KBP_DEBUG_BEGIN.
///
type

km_kbp_state_debug_key_info = record
  vk: uint16_t;
  modifier_state: uint16_t;
  character: char16_t;
end;

pkm_kbp_state_debug_key_info = ^km_kbp_state_debug_key_info;

km_kbp_state_debug_kmx_option_info = record
  store: Pointer;       // LPSTORE
  value: array[0..DEBUG_MAX_CONTEXT-1] of km_kbp_cp;  // value to be saved into the store
end;

pkm_kbp_state_debug_kmx_option_info = ^km_kbp_state_debug_kmx_option_info;

///
/// KMX processor data for each event. kmx_base.h defines the types that are
/// passed in here, read only. Warning: context may contain sentinel values
/// for markers, with the kmx UC_SENTINEL, CODE_CONTEXT, DEADKEY pattern.
///
/// The context value here will be an intermediate value, and may differ
/// from event to event as the context can be rewritten for each rule match.
///
/// Used in all event types except KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_END.
///

km_kbp_state_debug_kmx_info = record
  context: array [0..DEBUG_MAX_CONTEXT-1] of km_kbp_cp;     // The context matched by the rule (? may not need this?) // TODO: rename to context_matched
  group: Pointer;  // LPGROUP
  rule: Pointer;   // LPKEY
  store_offsets: array [0..DEBUG_STORE_OFFSETS_SIZE-1] of uint16_t;	// pairs--store, char position, terminated by 0xFFFF // TODO use a better structure here

  /// Track the actions index in the actions that will be returned to
  /// the debugger; the debugger uses this to determine when to
  /// execute the actions when single-stepping.
  first_action: uint16_t;
  option: km_kbp_state_debug_kmx_option_info;
end;

pkm_kbp_state_debug_kmx_info = ^km_kbp_state_debug_kmx_info;

///
/// A single debug event.
///
///

{$ALIGN 8}
km_kbp_state_debug_item = record
  _type: uint32_t; // TODO: _type as enum, 8 bit with padding? 32 bits is better optimized than 8 bits
  flags: uint32_t;
  key_info: km_kbp_state_debug_key_info;
  kmx_info: km_kbp_state_debug_kmx_info;
end;

pkm_kbp_state_debug_item = ^km_kbp_state_debug_item;

///
/// A single debug event.
///

km_kbp_debug_type = type uint32_t;

const
  KM_KBP_DEBUG_BEGIN = 0;
  //KM_KBP_DEBUG_BEGIN_ANSI: km_kbp_debug_type = 1, // not supported; instead rewrite ansi keyboards to Unicode with mcompile
  KM_KBP_DEBUG_GROUP_ENTER = 2;
  KM_KBP_DEBUG_GROUP_EXIT = 3;
  KM_KBP_DEBUG_RULE_ENTER = 4;
  KM_KBP_DEBUG_RULE_EXIT = 5;
  KM_KBP_DEBUG_MATCH_ENTER = 6;
  KM_KBP_DEBUG_MATCH_EXIT = 7;
  KM_KBP_DEBUG_NOMATCH_ENTER = 8;
  KM_KBP_DEBUG_NOMATCH_EXIT = 9;
  KM_KBP_DEBUG_END = 10;
  KM_KBP_DEBUG_SET_OPTION = 11;

// Flags for KM_KBP_DEBUG_GROUP_EXIT
const KM_KBP_DEBUG_FLAG_RECURSIVE_OVERFLOW = $0001;
const KM_KBP_DEBUG_FLAG_NOMATCH            = $0002;

// Flags for KM_KBP_DEBUG_BEGIN
// TODO: do we need this at all?
const KM_KBP_DEBUG_FLAG_UNICODE            = $0001; // Always set

// Flags for KM_KBP_DEBUG_END
const KM_KBP_DEBUG_FLAG_OUTPUTKEYSTROKE    = $0001;

///
/// Enable or disable debug tracing
///
/// @param state     Pointer to initialized state
/// @param value     Set to 1 to enable debugging, 0 to disable
///
/// @returns   KM_KBP_STATUS_OK on success
///
function km_kbp_state_debug_set(
  state: pkm_kbp_state;
  value: integer
): km_kbp_status; cdecl; external kmnkbp0 delayed;

///
/// Get current debug tracing status
///
/// @param state     Pointer to initialized state
///
/// @returns   1 if debugging is enabled, 0 otherwise
///
function km_kbp_state_debug_get(
  state: pkm_kbp_state
): uint8_t; cdecl; external kmnkbp0 delayed;

///
/// Read current debug trace log
///
/// @param state     Pointer to initialized state
/// @param num_items Pointer to variable to receive size of list; may be
///                  nullptr if not required.
///
/// @returns   pointer to read only array of debug item events,
///            with last entry guaranteed to be KM_KBP_DEBUG_END.
///
function km_kbp_state_debug_items(
  state: pkm_kbp_state;
  num_items: PCardinal
): pkm_kbp_state_debug_item; cdecl; external kmnkbp0 delayed;

implementation

initialization
  // Assertions generated by debug_api.cpp for win32 x86.
  // Usage: debug-api.exe --print-sizeof

  //keyboardprocessor.h:
  Assert(sizeof(km_kbp_context_item) = 8);
  Assert(sizeof(km_kbp_context_item) = 8);
  Assert(sizeof(km_kbp_action_item) = 12);
  Assert(sizeof(km_kbp_option_item) = 12);
  Assert(sizeof(km_kbp_keyboard_attrs) = 16);
  Assert(sizeof(km_kbp_attr) = 16);

  //keyboardprocessor_debug.h:
  Assert(sizeof(km_kbp_state_debug_item) = 432);
  Assert(sizeof(km_kbp_state_debug_key_info) = 6);
  Assert(sizeof(km_kbp_state_debug_kmx_info) = 416);
  Assert(sizeof(km_kbp_state_debug_kmx_option_info) = 164);
  Assert(sizeof(km_kbp_debug_type) = 4);
end.
