From: Tom Yu Date: Fri, 30 Mar 2007 00:54:36 +0000 (+0000) Subject: pull up r19189 from trunk X-Git-Tag: kfw-3.2.0-beta1~48 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=8b29607e7d59498fd8a087fb0615d42cfdfd63db;p=krb5.git pull up r19189 from trunk r19189@cathode-dark-space: jaltman | 2007-02-28 02:01:21 -0500 ticket: new subject: NIM Improved Alert Management component: windows This patch implements the new Alert Management functionality. Many improvements to avoid race conditions and improve resource tracking. ticket: 5452 version_fixed: 1.6.1 git-svn-id: svn://anonsvn.mit.edu/krb5/branches/krb5-1-6@19329 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/windows/identity/Makefile b/src/windows/identity/Makefile index e8fb50190..375bdce15 100644 --- a/src/windows/identity/Makefile +++ b/src/windows/identity/Makefile @@ -160,7 +160,7 @@ krb5plugin: plugincommon $(ECHO) -- Done with $@ !ifndef NO_KRB4 -doc: krb4plugin +finale: krb4plugin krb4plugin: plugincommon $(ECHO) -- Entering $@ diff --git a/src/windows/identity/apiversion.txt b/src/windows/identity/apiversion.txt index 9681ccc1c..4ff092b8d 100644 --- a/src/windows/identity/apiversion.txt +++ b/src/windows/identity/apiversion.txt @@ -220,8 +220,129 @@ Date=(TBD) #---------------------------------------------------------------- Version=7 AppVersion=1.1.9.0 -Date=(TBD) -# Released with KFW 3.2.0 +Date=Feb 16, 2007 +# Released with KFW 3.2 Alpha 1 +KHUI_ACTION_UICB -# Internal action to dispatch a UI callback \ No newline at end of file +# Internal action to dispatch a UI callback + ++WMNC_UPDATE_LAYOUT +# Used to update the layout and size of the dialogs during a new +# credentials operation. + +- NCDLG_TAB_HEIGHT, NCDLG_TAB_WIDTH +# No longer used + +- NCDLG_BBAR_WIDTH +# Moved to internal header file + ++ KCDB_IDENT_FLAG_UNKNOWN +# Used to indicate that an authority could not be contacted to +# determine the validity of an identity. + ++ khui_refresh_actions() +# Force a refresh of the application menus and toolbars. + ++ khui_action_lock() ++ khui_action_unlock() +# Synchronization of action and menu operations. + +! khui_alert +# Structure definition is now internal + +! khui_action +# Structure definition is now internal + ++ khui_alert_set_type() ++ khui_alert_set_ctx() ++ khui_alert_get_response() +# Additional functions to setup an alert. + +! kmq_message +# Added field "aborted" + ++ kmq_abort_call() ++ kmq_is_call_aborted() +# Added placeholders + +! kmq_message_ref +! kmq_queue +! kmq_msg_subscription +! kmq_msg_type +# Structure definition now internal + +! KMQ_MSG_TYPE_MAX +! KMQ_MAXCCH_TYPE_NAME +! KMQ_MAXCB_TYPE_NAME +! KMQ_MSG_SUB_MAGIC +! KMQ_RCPTTYPE_CB +! KMQ_RCPTTYPE_HWND +! KMQ_QUEUE_FLAG_DELETED +# Macros not internal + +#---------------------------------------------------------------- +Version=8 +AppVersion=1.1.10.0 +Date=(TBD) +# Released with (TBD) + +! hash_add(), hash_del(), hash_lookup(), hash_exist() +# 'key' parameter is now (const void *) + +! struct tag_has_bin, hash_bin +# 'key' member is now (const void *) + + ++WMNC_UPDATE_LAYOUT +# Used to update the layout and size of the dialogs during a new +# credentials operation. + +- NCDLG_TAB_HEIGHT, NCDLG_TAB_WIDTH +# No longer used + +- NCDLG_BBAR_WIDTH +# Moved to internal header file + ++ KCDB_IDENT_FLAG_UNKNOWN +# Used to indicate that an authority could not be contacted to +# determine the validity of an identity. + ++ khui_refresh_actions() +# Force a refresh of the application menus and toolbars. + ++ khui_action_lock() ++ khui_action_unlock() +# Synchronization of action and menu operations. + +! khui_alert +# Structure definition is now internal + +! khui_action +# Structure definition is now internal + ++ khui_alert_set_type() ++ khui_alert_set_ctx() ++ khui_alert_get_response() +# Additional functions to setup an alert. + +! kmq_message +# Added field "aborted" + ++ kmq_abort_call() ++ kmq_is_call_aborted() +# Added placeholders + +! kmq_message_ref +! kmq_queue +! kmq_msg_subscription +! kmq_msg_type +# Structure definition now internal + +! KMQ_MSG_TYPE_MAX +! KMQ_MAXCCH_TYPE_NAME +! KMQ_MAXCB_TYPE_NAME +! KMQ_MSG_SUB_MAGIC +! KMQ_RCPTTYPE_CB +! KMQ_RCPTTYPE_HWND +! KMQ_QUEUE_FLAG_DELETED +# Macros not internal diff --git a/src/windows/identity/config/Makefile.w2k b/src/windows/identity/config/Makefile.w2k index f2977786c..ffd1fc872 100644 --- a/src/windows/identity/config/Makefile.w2k +++ b/src/windows/identity/config/Makefile.w2k @@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1 # Version info NETIDMGR_VERSION_MAJOR=1 NETIDMGR_VERSION_MINOR=1 -NETIDMGR_VERSION_PATCH=9 +NETIDMGR_VERSION_PATCH=10 NETIDMGR_VERSION_AUX=0 NETIDMGR_RELEASEDESC= @@ -58,7 +58,7 @@ NETIDMGR_RELEASEDESC= # # Changes to the API version numbers should be documented in # apiversion.txt at the root of the source tree. -NETIDMGR_VERSION_API=7 +NETIDMGR_VERSION_API=8 # Minimum backwards compatible version. API versions from # NETIDMGR_VERSION_API_MINCOMPAT through NETIDMGR_VERSION_API @@ -208,6 +208,10 @@ khcwarn=/Wp64 khcwarn=$(khcwarn) /WX !endif +!if "$(CPU)" == "i386" +khdefines=$(khdefines) -D_USE_32BIT_TIME_T +!endif + #DEBUG_SYMBOLS ldebug=$(ldebug) /DEBUG cdebug=$(cdebug) -Os -Zi diff --git a/src/windows/identity/config/Makefile.w32 b/src/windows/identity/config/Makefile.w32 index e327a7075..d2281b6ae 100644 --- a/src/windows/identity/config/Makefile.w32 +++ b/src/windows/identity/config/Makefile.w32 @@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1 # Version info NETIDMGR_VERSION_MAJOR=1 NETIDMGR_VERSION_MINOR=1 -NETIDMGR_VERSION_PATCH=9 +NETIDMGR_VERSION_PATCH=10 NETIDMGR_VERSION_AUX=0 NETIDMGR_RELEASEDESC= @@ -58,7 +58,7 @@ NETIDMGR_RELEASEDESC= # # Changes to the API version numbers should be documented in # apiversion.txt at the root of the source tree. -NETIDMGR_VERSION_API=7 +NETIDMGR_VERSION_API=8 # Minimum backwards compatible version. API versions from # NETIDMGR_VERSION_API_MINCOMPAT through NETIDMGR_VERSION_API @@ -221,6 +221,10 @@ khcwarn=/Wp64 khcwarn=$(khcwarn) /WX !endif +!if "$(CPU)" == "i386" +khdefines=$(khdefines) -D_USE_32BIT_TIME_T +!endif + #DEBUG_SYMBOLS ldebug=$(ldebug) /DEBUG cdebug=$(cdebug) -Os -Zi diff --git a/src/windows/identity/doc/Makefile b/src/windows/identity/doc/Makefile index 85003999e..321d02fa1 100644 --- a/src/windows/identity/doc/Makefile +++ b/src/windows/identity/doc/Makefile @@ -64,5 +64,5 @@ CHM_FILE = "$(DOCDIR)\netiddev.chm" -$(HHC) $(DOCDIR)\html\index.hhp clean:: - $(RMDIR) /s /q $(DOCDIR)\html - $(RM) $(DOCDIR)\*.* + if exist "$(DOCDIR)/html" $(RMDIR) /s /q "$(DOCDIR)\html" + $(RM) "$(DOCDIR)\*.*" diff --git a/src/windows/identity/help/khhelp.h b/src/windows/identity/help/khhelp.h index 88f73c0b3..fbfef6047 100644 --- a/src/windows/identity/help/khhelp.h +++ b/src/windows/identity/help/khhelp.h @@ -28,6 +28,6 @@ #define IDH_NC_CANCEL 3002 #define IDH_NC_HELP 3003 #define IDH_NC_TABBUTTON 3004 -#define IDH_NC_OPTIONS 3005 +#define IDH_NC_ADVANCED 3005 #define IDH_NC_TABMAIN 3006 #define IDH_NC_SETDEF 3007 diff --git a/src/windows/identity/help/popups_newcreds.txt b/src/windows/identity/help/popups_newcreds.txt index 52c61244b..74a39360e 100644 --- a/src/windows/identity/help/popups_newcreds.txt +++ b/src/windows/identity/help/popups_newcreds.txt @@ -13,7 +13,7 @@ Cancels the new credentials operation. .topic IDH_NC_HELP Provides help for this dialog box. -.topic IDH_NC_OPTIONS +.topic IDH_NC_ADVANCED Expands the dialog and allows you to set additional options for the credentials that will be obtained by this dialog. diff --git a/src/windows/identity/help/popups_password.txt b/src/windows/identity/help/popups_password.txt index 39583252b..e92b30e91 100644 --- a/src/windows/identity/help/popups_password.txt +++ b/src/windows/identity/help/popups_password.txt @@ -10,7 +10,7 @@ Change the password for the selected identity. .topic IDH_NC_CANCEL Cancel the change password operation. -.topic IDH_NC_OPTIONS +.topic IDH_NC_ADVANCED Expand the dialog and make the option pages visible for the credential types for which you will be changing the password for. diff --git a/src/windows/identity/include/kherror.h b/src/windows/identity/include/kherror.h index f11e28510..ae381ff56 100644 --- a/src/windows/identity/include/kherror.h +++ b/src/windows/identity/include/kherror.h @@ -168,6 +168,11 @@ /*! \brief An incompatibility was found */ #define KHM_ERROR_INCOMPATIBLE (KHM_ERROR_BASE + 21) +/*! \brief The operation was put on hold + + A request was put on hold or postponed. */ +#define KHM_ERROR_HELD (KHM_ERROR_BASE + 22) + /*@}*/ /*kherror_codes*/ /*! \brief Tests whether a return value indicates success */ diff --git a/src/windows/identity/include/khlist.h b/src/windows/identity/include/khlist.h index 2eb85864a..44e0ffb68 100644 --- a/src/windows/identity/include/khlist.h +++ b/src/windows/identity/include/khlist.h @@ -116,6 +116,15 @@ if(!(pq)->head) (pq)->head = (pe); \ } while(0) +#define QPUSH(pq, pe) \ + do { \ + (pe)->next = NULL; \ + (pe)->prev = (pq)->head; \ + if((pq)->head) (pq)->head->next = (pe); \ + if(!(pq)->tail) (pq)->tail = (pe); \ + (pq)->head = (pe); \ + } while (0) + #define QGET(pq, ppe) \ do { \ *(ppe) = (pq)->head; \ diff --git a/src/windows/identity/kconfig/api.c b/src/windows/identity/kconfig/api.c index b3b4eb691..d944a4ce7 100644 --- a/src/windows/identity/kconfig/api.c +++ b/src/windows/identity/kconfig/api.c @@ -26,6 +26,7 @@ #include #include +#include #include kconf_conf_space * conf_root = NULL; @@ -86,6 +87,111 @@ void exit_kconf(void) { } } +#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL)) + +#include + +static void +khcint_dump_space(FILE * f, kconf_conf_space * sp) { + + kconf_conf_space * sc; + + fprintf(f, "c12\t[%S]\t[%S]\t%d\t0x%x\tWin(%s|%s)|%s\n", + ((sp->regpath) ? sp->regpath : L"!No Reg path"), + sp->name, + (int) sp->refcount, + (int) sp->flags, + ((sp->regkey_user)? "HKCU" : ""), + ((sp->regkey_machine)? "HKLM" : ""), + ((sp->schema)? "Schema" : "")); + + + sc = TFIRSTCHILD(sp); + while(sc) { + + khcint_dump_space(f, sc); + + sc = LNEXT(sc); + } +} + +KHMEXP void KHMAPI +khcint_dump_handles(FILE * f) { + if (khc_is_config_running()) { + kconf_handle * h, * sh; + + EnterCriticalSection(&cs_conf_handle); + EnterCriticalSection(&cs_conf_global); + + fprintf(f, "c00\t*** Active handles ***\n"); + fprintf(f, "c01\tHandle\tName\tFlags\tRegpath\n"); + + h = conf_handles; + while(h) { + kconf_conf_space * sp; + + sp = h->space; + + if (!khc_is_handle(h) || sp == NULL) { + + fprintf(f, "c02\t!!INVALID HANDLE!!\n"); + + } else { + + fprintf(f, "c02\t0x%p\t[%S]\t0x%x\t[%S]\n", + h, + sp->name, + h->flags, + sp->regpath); + + sh = khc_shadow(h); + + while(sh) { + + sp = sh->space; + + if (!khc_is_handle(sh) || sp == NULL) { + + fprintf(f, "c02\t0x%p:Shadow:0x%p\t[!!INVALID HANDLE!!]\n", + h, sh); + + } else { + + fprintf(f, "c02\t0x%p:Shadow:0x%p,[%S]\t0x%x\t[%S]\n", + h, sh, + sp->name, + sh->flags, + sp->regpath); + + } + + sh = khc_shadow(sh); + } + + } + + h = LNEXT(h); + } + + fprintf(f, "c03\t------ End ---------\n"); + + fprintf(f, "c10\t*** Active Configuration Spaces ***\n"); + fprintf(f, "c11\tReg path\tName\tRefcount\tFlags\tLayers\n"); + + khcint_dump_space(f, conf_root); + + fprintf(f, "c13\t------ End ---------\n"); + + LeaveCriticalSection(&cs_conf_global); + LeaveCriticalSection(&cs_conf_handle); + + } else { + fprintf(f, "c00\t------- KHC Configuration not running -------\n"); + } +} + +#endif + /* obtains cs_conf_handle/cs_conf_global */ kconf_handle * khcint_handle_from_space(kconf_conf_space * s, khm_int32 flags) @@ -180,6 +286,29 @@ khcint_space_hold(kconf_conf_space * s) { LeaveCriticalSection(&cs_conf_global); } +/* called with cs_conf_global */ +void +khcint_try_free_space(kconf_conf_space * s) { + + if (TFIRSTCHILD(s) == NULL && + s->refcount == 0 && + s->schema == NULL) { + + kconf_conf_space * p; + + p = TPARENT(s); + + if (p == NULL) + return; + + TDELCHILD(p, s); + + khcint_free_space(s); + + khcint_try_free_space(p); + } +} + /* obtains cs_conf_global */ void khcint_space_release(kconf_conf_space * s) { @@ -200,6 +329,15 @@ khcint_space_release(kconf_conf_space * s) { (KCONF_SPACE_FLAG_DELETE_M | KCONF_SPACE_FLAG_DELETE_U)) { khcint_remove_space(s, s->flags); + } else { +#ifdef USE_TRY_FREE + /* even if the refcount is zero, we shouldn't free a + configuration space just yet since that doesn't play + well with the configuration space enumeration mechanism + which expects the spaces to dangle around if there is a + corresponding registry key or schema. */ + khcint_try_free_space(s); +#endif } } @@ -673,10 +811,10 @@ khcint_free_space(kconf_conf_space * r) { if(!r) return; - LPOP(&r->children, &c); + TPOPCHILD(r, &c); while(c) { khcint_free_space(c); - LPOP(&r->children, &c); + TPOPCHILD(r, &c); } if(r->name) @@ -847,8 +985,8 @@ khc_open_space(khm_handle parent, const wchar_t * cspace, khm_int32 flags, flags |= KCONF_FLAG_USER | KCONF_FLAG_MACHINE | KCONF_FLAG_SCHEMA; if(cspace == NULL) { - khcint_space_release(p); *result = (khm_handle) khcint_handle_from_space(p, flags); + khcint_space_release(p); return KHM_ERROR_SUCCESS; } @@ -897,6 +1035,9 @@ khc_open_space(khm_handle parent, const wchar_t * cspace, khm_int32 flags, } else *result = NULL; + if (c) + khcint_space_release(c); + return rv; } @@ -2318,7 +2459,7 @@ khc_unload_schema(khm_handle conf, const kconf_schema * schema) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_conf_global); - rv = khcint_unload_schema_i(conf, schema, 0, NULL); + rv = khcint_unload_schema_i(conf, schema, 0, NULL); LeaveCriticalSection(&cs_conf_global); return rv; diff --git a/src/windows/identity/kcreddb/credset.c b/src/windows/identity/kcreddb/credset.c index dda9817d3..141e17cd2 100644 --- a/src/windows/identity/kcreddb/credset.c +++ b/src/windows/identity/kcreddb/credset.c @@ -1047,7 +1047,8 @@ kcdb_credset_unseal(khm_handle credset) { } -/* wrapper for qsort and also parameter gobbling FSM. */ +/* wrapper for qsort and also parameter gobbling FSM. Access to this + function is serialized via cs_credset. */ int __cdecl kcdb_creds_comp_wrapper(const void * a, const void * b) { @@ -1091,12 +1092,16 @@ kcdb_credset_sort(khm_handle credset, assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM)); #endif + EnterCriticalSection(&cs_credset); + kcdb_creds_comp_wrapper(rock, NULL); kcdb_creds_comp_wrapper(NULL, (void *) comp); qsort(cs->clist, cs->nclist, sizeof(kcdb_credset_credref), kcdb_creds_comp_wrapper); + LeaveCriticalSection(&cs_credset); + LeaveCriticalSection(&(cs->cs)); return code; } diff --git a/src/windows/identity/kcreddb/identity.c b/src/windows/identity/kcreddb/identity.c index 3f6020682..326d0258a 100644 --- a/src/windows/identity/kcreddb/identity.c +++ b/src/windows/identity/kcreddb/identity.c @@ -55,7 +55,7 @@ kcdb_identity_set_provider(khm_handle sub) EnterCriticalSection(&cs_ident); if (sub != kcdb_ident_sub) { if(kcdb_ident_sub != NULL) { - kmq_post_sub_msg(kcdb_ident_sub, + kmq_send_sub_msg(kcdb_ident_sub, KMSG_IDENT, KMSG_IDENT_EXIT, 0, @@ -425,10 +425,12 @@ kcdb_identity_set_flags(khm_handle vid, id->flags = (id->flags & ~mask) | (flag & mask); - if (flag & KCDB_IDENT_FLAG_VALID) - id->flags &= ~KCDB_IDENT_FLAG_INVALID; - if (flag & KCDB_IDENT_FLAG_INVALID) - id->flags &= ~KCDB_IDENT_FLAG_VALID; + if (flag & KCDB_IDENT_FLAG_VALID) { + id->flags &= ~(KCDB_IDENT_FLAG_INVALID | KCDB_IDENT_FLAG_UNKNOWN); + } + if (flag & KCDB_IDENT_FLAG_INVALID) { + id->flags &= ~(KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_UNKNOWN); + } newflags = id->flags; @@ -466,7 +468,9 @@ kcdb_identity_get_flags(khm_handle vid, id = (kcdb_identity *) vid; + EnterCriticalSection(&cs_ident); *flags = id->flags; + LeaveCriticalSection(&cs_ident); return KHM_ERROR_SUCCESS; } diff --git a/src/windows/identity/kcreddb/kcreddb.h b/src/windows/identity/kcreddb/kcreddb.h index 060f556ac..4b15a245f 100644 --- a/src/windows/identity/kcreddb/kcreddb.h +++ b/src/windows/identity/kcreddb/kcreddb.h @@ -232,11 +232,21 @@ Functions, macros etc. for manipulating identities. */ #define KCDB_IDENT_FLAG_STICKY 0x00000800L +/*! \brief Unknown state + + The validity of the identity cannot be determined. This usually + means that an authority could not be contacted. This flag is to + be treated as transient. If ::KCDB_IDENT_FLAG_INVALID or + ::KCDB_IDENT_FLAG_VALID is set for the identity, this flag is to + be ignored. + */ +#define KCDB_IDENT_FLAG_UNKNOWN 0x00001000L + /*! \brief Read/write flags mask. A bitmask that correspond to all the read/write flags in the mask. */ -#define KCDB_IDENT_FLAGMASK_RDWR 0x00000fffL +#define KCDB_IDENT_FLAGMASK_RDWR 0x00001fffL /*@}*/ @@ -431,7 +441,9 @@ kcdb_identity_delete(khm_handle id); If ::KCDB_IDENT_FLAG_INVALID is set using this function, then the ::KCDB_IDENT_FLAG_VALID will be automatically reset, and vice versa. Resetting either bit does not undo this change, and will - leave the identity's validity unspecified. + leave the identity's validity unspecified. Setting either of + ::KCDB_IDENT_FLAG_INVALID or ::KCDB_IDENT_FLAG_VALID will + automatically reset ::KCDB_IDENT_FLAG_UNKNOWN. Note that setting or resetting certain flags have other semantic side-effects: diff --git a/src/windows/identity/kmm/kmm_registrar.c b/src/windows/identity/kmm/kmm_registrar.c index f93363e58..636e8579f 100644 --- a/src/windows/identity/kmm/kmm_registrar.c +++ b/src/windows/identity/kmm/kmm_registrar.c @@ -131,6 +131,9 @@ khm_boolean KHMAPI kmmint_reg_cb(khm_int32 msg_type, callback routine ( kmmint_reg_cb() ) */ DWORD WINAPI kmmint_registrar(LPVOID lpParameter) { + + PDESCTHREAD(L"KMM Registrar", L"KMM"); + tid_registrar = GetCurrentThreadId(); kmq_subscribe(KMSG_KMM, kmmint_reg_cb); @@ -140,6 +143,9 @@ DWORD WINAPI kmmint_registrar(LPVOID lpParameter) while(KHM_SUCCEEDED(kmq_dispatch(INFINITE))); + kmq_unsubscribe(KMSG_KMM, kmmint_reg_cb); + kmq_unsubscribe(KMSG_SYSTEM, kmmint_reg_cb); + ExitThread(0); /* not reached */ return 0; @@ -156,6 +162,8 @@ DWORD WINAPI kmmint_plugin_broker(LPVOID lpParameter) DWORD rv = 0; kmm_plugin_i * p = (kmm_plugin_i *) lpParameter; + PDESCTHREAD(p->p.name, L"KMM"); + _begin_task(0); _report_mr1(KHERR_NONE, MSG_PB_START, _cstr(p->p.name)); _describe(); @@ -324,8 +332,13 @@ void kmmint_init_plugin(kmm_plugin_i * p) { from the initial attempt to start the plugin. Undo the hold we did a few lines earlier. */ kmm_release_plugin(kmm_handle_from_plugin(p)); + /* same for the plugin count for the module. */ +#ifdef DEBUG + assert(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT); +#endif p->module->plugin_count--; + p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT; } p->state = KMM_PLUGIN_STATE_PREINIT; @@ -435,6 +448,10 @@ void kmmint_init_plugin(kmm_plugin_i * p) { } while(FALSE); +#ifdef DEBUG + assert(!(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT)); +#endif + p->flags |= KMM_PLUGIN_FLAG_IN_MODCOUNT; p->module->plugin_count++; LeaveCriticalSection(&cs_kmm); @@ -485,9 +502,18 @@ _exit: _dupstr(p->p.name), _int32(p->state)); _end_task(); + +#ifdef ASYNC_PLUGIN_UNLOAD_ON_FAILURE + kmm_hold_plugin(kmm_handle_from_plugin(p)); kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p); + +#else + + kmmint_exit_plugin(p); + +#endif } /*! \internal @@ -522,14 +548,15 @@ void kmmint_exit_plugin(kmm_plugin_i * p) { EnterCriticalSection(&cs_kmm); /* undo reference count done in kmmint_init_plugin() */ - if(p->state == KMM_PLUGIN_STATE_EXITED || - p->state == KMM_PLUGIN_STATE_HOLD) { + if(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT) { np = --(p->module->plugin_count); + p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT; } else { - /* the plugin was never active. We can't base a module unload - decision on np */ + /* the plugin was not included in the module's plugin_count. + We can't base a decision to unload the module based on this + plugin exiting. */ np = TRUE; } @@ -915,16 +942,23 @@ void kmmint_exit_module(kmm_module_i * m) { p = kmm_listed_plugins; while(p) { - if(p->module == m) { + if(p->module == m && + (p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT)) { + kmm_hold_plugin(kmm_handle_from_plugin(p)); kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p); np++; + } p = LNEXT(p); } +#ifdef DEBUG + assert(np == m->plugin_count); +#endif + if(np > 0) { /* we have to go back and wait for the plugins to exit. when the last plugin exits, it automatically posts @@ -933,6 +967,21 @@ void kmmint_exit_module(kmm_module_i * m) { LeaveCriticalSection(&cs_kmm); return; } + + } else { + +#ifdef DEBUG + assert(m->plugin_count == 0 || + m->state == KMM_MODULE_STATE_EXITPLUG); +#endif + + /* if there are still plug-ins waiting to be unloaded, then we + have to go back and wait for them to finish. Once they are + done, kmmint_exit_module() will get called again. */ + if (m->plugin_count > 0) { + LeaveCriticalSection(&cs_kmm); + return; + } } if(m->flags & KMM_MODULE_FLAG_INITP) { @@ -968,6 +1017,14 @@ void kmmint_exit_module(kmm_module_i * m) { m->h_module = NULL; m->h_resource = NULL; + + if (m->flags & KMM_MODULE_FLAG_LOADED) { +#ifdef DEBUG + assert(kmm_active_modules > 0); +#endif + kmm_active_modules--; + } + m->flags = 0; /* release the hold obtained in kmmint_init_module() */ @@ -975,13 +1032,6 @@ void kmmint_exit_module(kmm_module_i * m) { /* Last but not least, now see if there are any modules left that are running. If not, we can safely signal an exit. */ - -#ifdef DEBUG - assert(kmm_active_modules > 0); -#endif - - kmm_active_modules--; - if (kmm_active_modules == 0) { SetEvent(evt_exit); } diff --git a/src/windows/identity/kmm/kmminternal.h b/src/windows/identity/kmm/kmminternal.h index 41eaa7365..38fce2422 100644 --- a/src/windows/identity/kmm/kmminternal.h +++ b/src/windows/identity/kmm/kmminternal.h @@ -40,6 +40,9 @@ #include #include #include + +#define NOEXPORT + #include #include #include @@ -104,6 +107,9 @@ typedef struct kmm_module_i_t { (option specified in configuration)*/ #define KMM_MODULE_FLAG_NOUNLOAD 0x00000800 +/* the module has been removed from the active modules list. */ +#define KMM_MODULE_FLAG_INACTIVE 0x00001000 + typedef struct kmm_plugin_i_t { kmm_plugin_reg p; @@ -142,13 +148,17 @@ typedef struct kmm_plugin_i_t { /* the plugin is in the module's plugin list */ #define KMM_PLUGIN_FLAG_IN_MODLIST 0x00000004 +/* the plugin has been included in the pending_plugins count. */ #define KMM_PLUGIN_FLAG_IN_QUEUE 0x00000010 +/* the plugin is included in the module's plugin count */ +#define KMM_PLUGIN_FLAG_IN_MODCOUNT 0x00000020 + /* the plugin is disabled by the user (option specified in configuration) */ /* (this is defined in kmm.h) - #define KMM_PLUGIN_FLAG_DISABLED 0x0400 + #define KMM_PLUGIN_FLAG_DISABLED 0x00000400 */ diff --git a/src/windows/identity/kmq/consumer.c b/src/windows/identity/kmq/consumer.c index ed7d548f1..d6041779f 100644 --- a/src/windows/identity/kmq/consumer.c +++ b/src/windows/identity/kmq/consumer.c @@ -36,6 +36,70 @@ kmq_message_ref * kmq_msg_ref_free = NULL; /* ad-hoc subscriptions */ kmq_msg_subscription * kmq_adhoc_subs = NULL; +#ifdef DEBUG + +#include + +void +kmqint_dump_consumer(FILE * f) { + kmq_message_ref * r; + kmq_msg_subscription * s; + + int n_free = 0; + int n_adhoc = 0; + + EnterCriticalSection(&cs_kmq_msg_ref); + + fprintf(f, "qc0\t*** Free Message References ***\n"); + + fprintf(f, "qc1\tAddress\n"); + + r = kmq_msg_ref_free; + while(r) { + n_free ++; + + fprintf(f, "qc2\t0x%p\n", r); + + r = LNEXT(r); + } + + fprintf(f, "qc3\tTotal free message references : %d\n", n_free); + + fprintf(f, "qc4\t--- End ---\n"); + + LeaveCriticalSection(&cs_kmq_msg_ref); + + EnterCriticalSection(&cs_kmq_global); + + fprintf(f, "qc5\t*** Adhoc Message Subscriptions ***\n"); + + fprintf(f, "qc6\tAddress\tMsg Type\tRcpt Type\tRcpt\tQueue\n"); + + s = kmq_adhoc_subs; + + while(s) { + n_adhoc ++; + + fprintf(f, "qc7\t0x%p\t%d\t%s\t0x%p\t0x%p\n", + s, + s->type, + (s->rcpt_type == KMQ_RCPTTYPE_CB)?"CALLBACK":"HWND", + (s->rcpt_type == KMQ_RCPTTYPE_CB) ? (void *) s->recipient.cb: (void *) s->recipient.hwnd, + s->queue); + + s = LNEXT(s); + } + + fprintf(f, "qc8\tTotal ad-hoc subscriptions : %d\n", n_adhoc); + + fprintf(f, "qc9\t--- End ---\n"); + + LeaveCriticalSection(&cs_kmq_global); + +} + +#endif + /*! \internal \brief Get a message ref object \note called with cs_kmq_msg_ref held */ @@ -89,9 +153,15 @@ kmq_queue * kmqint_get_thread_queue(void) { */ void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r) { EnterCriticalSection(&q->cs); - QGET(q,r); - if(QTOP(q)) - SetEvent(q->wait_o); + + if (q->flags & KMQ_QUEUE_FLAG_DELETED) { + *r = NULL; + } else { + QGET(q,r); + if(QTOP(q)) + SetEvent(q->wait_o); + } + LeaveCriticalSection(&q->cs); } @@ -102,6 +172,13 @@ void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r) { void kmqint_post_queue(kmq_queue * q, kmq_message *m) { kmq_message_ref *r; + EnterCriticalSection(&q->cs); + if (q->flags & KMQ_QUEUE_FLAG_DELETED) { + LeaveCriticalSection(&q->cs); + return; + } + LeaveCriticalSection(&q->cs); + EnterCriticalSection(&cs_kmq_msg_ref); r = kmqint_get_message_ref(); LeaveCriticalSection(&cs_kmq_msg_ref); @@ -142,11 +219,8 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send if (IsBadCodePtr(s->recipient.cb)) { rv = KHM_ERROR_INVALID_OPERATION; } else { - if (IsBadCodePtr(s->recipient.cb)) - rv = KHM_ERROR_INVALID_OPERATION; - else - rv = s->recipient.cb(m->type, m->subtype, - m->uparam, m->vparam); + rv = s->recipient.cb(m->type, m->subtype, + m->uparam, m->vparam); } m->refcount--; if(KHM_SUCCEEDED(rv)) @@ -154,6 +228,14 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send else m->nFailed++; } else { + + EnterCriticalSection(&q->cs); + if (q->flags & KMQ_QUEUE_FLAG_DELETED) { + LeaveCriticalSection(&q->cs); + return; + } + LeaveCriticalSection(&q->cs); + EnterCriticalSection(&cs_kmq_msg_ref); r = kmqint_get_message_ref(); LeaveCriticalSection(&cs_kmq_msg_ref); @@ -173,8 +255,6 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send #ifdef _WIN32 else if(s->rcpt_type == KMQ_RCPTTYPE_HWND) { - m->refcount++; - if(try_send && GetCurrentThreadId() == GetWindowThreadProcessId(s->recipient.hwnd, NULL)) { @@ -185,11 +265,24 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send message has completed when (m->nCompleted + m->nFailed == m->nSent). Therefore, we only increment nSent after the message is sent. */ + + m->refcount++; + + /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch() + handlers decrement the reference count on the message + when they are done. */ SendMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, m->type, (LPARAM) m); + m->nSent++; + } else { m->nSent++; + m->refcount++; + + /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch() + handlers decrement the reference count on the message + when they are done. */ PostMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, m->type, (LPARAM) m); } @@ -202,8 +295,6 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send deleted an ad-hoc subscription. */ #ifdef DEBUG assert(FALSE); -#else - return; #endif } } @@ -401,6 +492,11 @@ KHMEXP LRESULT KHMAPI kmq_wm_dispatch(LPARAM lparm, kmq_callback_t cb) { return TRUE; } +KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void) { + /* TODO: Implement this */ + return FALSE; +} + /*! \internal \note Obtains ::cs_kmq_global, kmq_queue::cs, ::cs_kmq_msg_ref, ::cs_kmq_msg, @@ -428,6 +524,12 @@ KHMEXP khm_int32 KHMAPI kmq_dispatch(kmq_timer timeout) { if (m->err_ctx) kherr_push_context(m->err_ctx); + /* TODO: before dispatching the message, the message being + dispatched for this thread needs to be stored so that + it can be looked up in kmq_is_call_aborted(). This + needs to happen in kmq_wm_dispatch() and + kmq_wm_begin() as well. */ + /* dispatch */ rv = r->recipient(m->type, m->subtype, m->uparam, m->vparam); diff --git a/src/windows/identity/kmq/init.c b/src/windows/identity/kmq/init.c index 00b8f6f4f..875e3eaf3 100644 --- a/src/windows/identity/kmq/init.c +++ b/src/windows/identity/kmq/init.c @@ -115,11 +115,58 @@ void kmqint_detach_this_thread(void) { q = (kmq_queue *) TlsGetValue(kmq_tls_queue); if(q) { + kmq_message_ref * r; + kmq_message * m; + EnterCriticalSection(&q->cs); - q->flags |= KMQ_QUEUE_FLAG_DELETED; + + if (q->flags & KMQ_QUEUE_FLAG_DETACHING) { +#ifdef DEBUG + assert(FALSE); +#endif + LeaveCriticalSection(&q->cs); + return; + } + + q->flags |= KMQ_QUEUE_FLAG_DELETED | KMQ_QUEUE_FLAG_DETACHING; + + QGET(q, &r); + while(r) { + + m = r->msg; + + LeaveCriticalSection(&q->cs); + + EnterCriticalSection(&cs_kmq_msg); + EnterCriticalSection(&cs_kmq_msg_ref); + kmqint_put_message_ref(r); + LeaveCriticalSection(&cs_kmq_msg_ref); + + m->nFailed++; + if(m->nCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + EnterCriticalSection(&q->cs); + + QGET(q, &r); + } + + CloseHandle(q->wait_o); + + q->wait_o = NULL; + + q->flags &= ~KMQ_QUEUE_FLAG_DETACHING; + LeaveCriticalSection(&q->cs); - /* TODO: free up the queued messages */ + /* For now, we don't free the queue. */ + + /* TODO: before we can free the queue, we have to go through + all the message type subscriptions and ad-hoc subscriptions + and make sure no subscriptions exist which refer to this + message queue. */ } } @@ -139,6 +186,8 @@ DWORD WINAPI kmqint_completion_thread_proc(LPVOID p) { kmq_message * m; kherr_context * ctx; + PDESCTHREAD(L"Msg completion thread", L"KMQ"); + EnterCriticalSection(&cs_compl); do { @@ -249,3 +298,16 @@ KHMEXP khm_int32 KHMAPI kmq_exit(void) { return KHM_ERROR_SUCCESS; } + +#ifdef DEBUG + +void kmqint_dump_consumer(FILE * f); +void kmqint_dump_publisher(FILE * f); + + +KHMEXP void KHMAPI kmqint_dump(FILE * f) { + kmqint_dump_consumer(f); + kmqint_dump_publisher(f); +} + +#endif diff --git a/src/windows/identity/kmq/kmq.h b/src/windows/identity/kmq/kmq.h index fef6a3096..46ce74f3b 100644 --- a/src/windows/identity/kmq/kmq.h +++ b/src/windows/identity/kmq/kmq.h @@ -112,92 +112,18 @@ typedef struct tag_kmq_message { kherr_context * err_ctx; /*!< Error context for the message */ - khm_int32 refcount; + khm_boolean aborted; /*!< TRUE if the message has been + aborted. */ - LDCL(struct tag_kmq_message); + khm_int32 refcount; /*!< Internal use */ + + LDCL(struct tag_kmq_message); /*!< Internal use */ } kmq_message; /*! \brief A handle to a call */ typedef kmq_message *kmq_call; -/*! \brief Message reference */ -typedef struct tag_kmq_message_ref { - kmq_message * msg; /*!< Message that we are referring - to */ - kmq_callback_t recipient; /*!< The recipient of the message */ - - LDCL(struct tag_kmq_message_ref); -} kmq_message_ref; - -/*! \brief Message queue - - Each thread gets its own message queue. When a message is - broadcast to which there is a subscriber in a particular thread, a - reference to the message is placed in the message queue of the - thread. The dispatch procedure then dispatches the message as - described in the message reference. -*/ -typedef struct tag_kmq_queue { - kmq_thread_id thread; /*!< The thread id */ - - CRITICAL_SECTION cs; - HANDLE wait_o; - - khm_int32 load; /*!< Number of messages waiting to be - processed on this message queue. */ - kmq_timer last_post; /*!< Time the last message was - received */ - - khm_int32 flags; /*!< Flags. Currently, it's just KMQ_QUEUE_FLAG_DELETED */ - - /*Q*/ - QDCL(kmq_message_ref); /*!< Queue of message references */ - - /*Lnode*/ - LDCL(struct tag_kmq_queue); -} kmq_queue; - -#define KMQ_QUEUE_FLAG_DELETED 0x0008 - -/*! \brief Message subscription - - A subscription binds a recipient with a message type. These are - specific to a thread. I.e. a subscription that was made in one - thread will not receive messages in the context of another thread. -*/ -typedef struct tag_kmq_msg_subscription { - khm_int32 magic; /*!< Magic number. Should always be - ::KMQ_MSG_SUB_MAGIC */ - khm_int32 type; /*!< Type of message */ - khm_int32 rcpt_type; /*!< Type of recipient. One of - ::KMQ_RCPTTYPE_CB or - ::KMQ_RCPTTYPE_HWND */ - union { - kmq_callback_t cb; /*!< Callback if the subscription is - of callback type */ - HWND hwnd; /*!< Window handle if the subscription - is a windows message type */ - } recipient; - - kmq_queue * queue; /*!< Associated queue */ - - /*lnode*/ - LDCL(struct tag_kmq_msg_subscription); -} kmq_msg_subscription; - -#define KMQ_MSG_SUB_MAGIC 0x3821b58e - -/*! \brief Callback recipient type - - The recipient is a callback function */ -#define KMQ_RCPTTYPE_CB 1 - -/*! \brief Windows recipient type - - The recipient is a window */ -#define KMQ_RCPTTYPE_HWND 2 - /* publishers */ /*! \brief A completion handler for a message @@ -214,43 +140,14 @@ typedef struct tag_kmq_msg_subscription { */ typedef void (KHMAPI *kmq_msg_completion_handler)(kmq_message *); -/*! \brief A message type - */ -typedef struct tag_kmq_msg_type { - khm_int32 id; /*!< Identifier for the message - type. */ - kmq_msg_subscription * subs; /*!< The list of subscriptions */ - kmq_msg_completion_handler completion_handler; /*!< Completion - handler for the message type */ - - wchar_t * name; /*!< Name of the message type for - named types. Message type names are - language independant. */ - - /*Lnode*/ - LDCL(struct tag_kmq_msg_type); -} kmq_msg_type; - -/*! \brief The maximum number of message types - */ -#define KMQ_MSG_TYPE_MAX 255 - -/*! \brief Maximum number of characters in a message type name - - The count includes the terminating NULL - */ -#define KMQ_MAXCCH_TYPE_NAME 256 - -/*! \brief Maximum number of bytes in a message type name - - Type count includes the terminating NULL - */ -#define KMQ_MAXCB_TYPE_NAME (KMQ_MAXCCH_TYPE_NAME * sizeof(wchar_t)) +#ifdef NOEXPORT KHMEXP khm_int32 KHMAPI kmq_init(void); KHMEXP khm_int32 KHMAPI kmq_exit(void); +#endif + /*! \brief Register a message type Registers a custom message type. The \a name parameter specifies @@ -750,6 +647,46 @@ KHMEXP khm_boolean KHMAPI kmq_has_completed(kmq_call call); */ KHMEXP khm_int32 KHMAPI kmq_wait(kmq_call call, kmq_timer timeout); +/*! \brief Abort a call + + Abort a pending call. The call handle should have been obtained + using a prior call to kmq_post_message_ex(). + + Note that this function may not abort the call immediately. It + merely marks the message as being in an aborted state. It is upto + the individual handlers of the message to check if the message has + been aborted and act accordingly. + + The handlers are expected to call kmq_is_call_aborted() + periodicially during the processing of specially lengthy + operations during the course of handling a message. That function + will return \a TRUE if the last dispatched message is now in an + aborted state. In which case, the handler is expected to abort + handling the message and return control to the dispatcher. + */ +KHMEXP khm_int32 KHMAPI kmq_abort_call(kmq_call call); + +/*! \brief Check if the last dispatched message was aborted + + The sender of a message may abort it using a call to + kmq_abort_call(). This function checks if the last dispatched + message was aborted. + + A handler of a message is expected to call this function + periodically if handling the message is going to take a specially + long time (e.g. more than 5 or 10 seconds). If the message is + found to be aborted, the handler is expected to abort handling the + message, perform any necessary cleanup and return control to the + dispatcher. + + Doing this allows operations like new credentials acquisition to + be cleanly aborted by the user if she so wishes. Otherwise, + Network Identity Manager has to wait for the message to complete + processing since it has no means of cleanly terminating an + executing plug-in thread. +*/ +KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void); + /*! \brief Sets the completion handler for a specified message type. \note Only one completion handler can exist for one message type. diff --git a/src/windows/identity/kmq/kmqinternal.h b/src/windows/identity/kmq/kmqinternal.h index aeaf3366c..1d6196b30 100644 --- a/src/windows/identity/kmq/kmqinternal.h +++ b/src/windows/identity/kmq/kmqinternal.h @@ -33,9 +33,135 @@ #include #include #include + +#define NOEXPORT + #include #include + + + +/*! \brief Message reference */ +typedef struct tag_kmq_message_ref { + kmq_message * msg; /*!< Message that we are referring + to */ + kmq_callback_t recipient; /*!< The recipient of the message */ + + LDCL(struct tag_kmq_message_ref); +} kmq_message_ref; + + + + +/*! \brief Message queue + + Each thread gets its own message queue. When a message is + broadcast to which there is a subscriber in a particular thread, a + reference to the message is placed in the message queue of the + thread. The dispatch procedure then dispatches the message as + described in the message reference. +*/ +typedef struct tag_kmq_queue { + kmq_thread_id thread; /*!< The thread id */ + + CRITICAL_SECTION cs; + HANDLE wait_o; + + khm_int32 load; /*!< Number of messages waiting to be + processed on this message queue. */ + kmq_timer last_post; /*!< Time the last message was + received */ + + khm_int32 flags; /*!< Flags. Currently, it's just KMQ_QUEUE_FLAG_DELETED */ + + /*Q*/ + QDCL(kmq_message_ref); /*!< Queue of message references */ + + /*Lnode*/ + LDCL(struct tag_kmq_queue); +} kmq_queue; + +#define KMQ_QUEUE_FLAG_DELETED 0x00000008 +#define KMQ_QUEUE_FLAG_DETACHING 0x00000010 + +/*! \brief Message subscription + + A subscription binds a recipient with a message type. These are + specific to a thread. I.e. a subscription that was made in one + thread will not receive messages in the context of another thread. +*/ +typedef struct tag_kmq_msg_subscription { + khm_int32 magic; /*!< Magic number. Should always be + ::KMQ_MSG_SUB_MAGIC */ + khm_int32 type; /*!< Type of message */ + khm_int32 rcpt_type; /*!< Type of recipient. One of + ::KMQ_RCPTTYPE_CB or + ::KMQ_RCPTTYPE_HWND */ + union { + kmq_callback_t cb; /*!< Callback if the subscription is + of callback type */ + HWND hwnd; /*!< Window handle if the subscription + is a windows message type */ + } recipient; + + kmq_queue * queue; /*!< Associated queue */ + + /*lnode*/ + LDCL(struct tag_kmq_msg_subscription); +} kmq_msg_subscription; + +#define KMQ_MSG_SUB_MAGIC 0x3821b58e + +/*! \brief Callback recipient type + + The recipient is a callback function */ +#define KMQ_RCPTTYPE_CB 1 + +/*! \brief Windows recipient type + + The recipient is a window */ +#define KMQ_RCPTTYPE_HWND 2 + + + + +/*! \brief A message type + */ +typedef struct tag_kmq_msg_type { + khm_int32 id; /*!< Identifier for the message + type. */ + kmq_msg_subscription * subs; /*!< The list of subscriptions */ + kmq_msg_completion_handler completion_handler; /*!< Completion + handler for the message type */ + + wchar_t * name; /*!< Name of the message type for + named types. Message type names are + language independant. */ + + /*Lnode*/ + LDCL(struct tag_kmq_msg_type); +} kmq_msg_type; + +/*! \brief The maximum number of message types + */ +#define KMQ_MSG_TYPE_MAX 255 + +/*! \brief Maximum number of characters in a message type name + + The count includes the terminating NULL + */ +#define KMQ_MAXCCH_TYPE_NAME 256 + +/*! \brief Maximum number of bytes in a message type name + + Type count includes the terminating NULL + */ +#define KMQ_MAXCB_TYPE_NAME (KMQ_MAXCCH_TYPE_NAME * sizeof(wchar_t)) + + + + #define KMQ_CONF_SPACE_NAME L"KMQ" #define KMQ_CONF_QUEUE_DEAD_TIMEOUT_NAME L"QueueDeadTimeout" #define KMQ_CONF_CALL_DEAD_TIMEOUT_NAME L"CallDeadTimeout" @@ -69,6 +195,7 @@ void kmqint_post_queue(kmq_queue * q, kmq_message *m); void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send); kmq_queue * kmqint_get_thread_queue(void); void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r); +void kmqint_put_message_ref(kmq_message_ref * r); /* publisher */ extern CRITICAL_SECTION cs_kmq_msg; diff --git a/src/windows/identity/kmq/msgtype.c b/src/windows/identity/kmq/msgtype.c index 3fd31ec17..1b0868d83 100644 --- a/src/windows/identity/kmq/msgtype.c +++ b/src/windows/identity/kmq/msgtype.c @@ -81,9 +81,30 @@ int kmqint_notify_msg_completion(kmq_message * m) { /* called with cs_mkq_global && cs_kmq_types held */ void kmqint_free_msg_type(int t) { - /*TODO: free the message type*/ - /* must set handler to NULL before freeing type */ - /* must set msg_type[t] = NULL before starting to free type */ + kmq_msg_type * pt; + kmq_msg_subscription * s; + + pt = msg_types[t]; + + msg_types[t] = NULL; + + if (pt == NULL) + return; + + /* all the subscriptions attached to a message type are owned by + the message type */ + LPOP(&pt->subs, &s); + while(s) { + s->magic = 0; + + PFREE(s); + + LPOP(&pt->subs, &s); + } + + pt->completion_handler = NULL; + + PFREE(pt); } /*! \internal diff --git a/src/windows/identity/kmq/publisher.c b/src/windows/identity/kmq/publisher.c index af1f55566..66360fb50 100644 --- a/src/windows/identity/kmq/publisher.c +++ b/src/windows/identity/kmq/publisher.c @@ -30,6 +30,66 @@ CRITICAL_SECTION cs_kmq_msg; kmq_message * msg_free = NULL; kmq_message * msg_active = NULL; +#ifdef DEBUG + +#include + +void +kmqint_dump_publisher(FILE * f) { + + int n_free = 0; + int n_active = 0; + kmq_message * m; + + EnterCriticalSection(&cs_kmq_msg); + + fprintf(f, "qp0\t*** Free Messages ***\n"); + fprintf(f, "qp1\tAddress\n"); + + m = msg_free; + while(m) { + n_free++; + + fprintf(f, "qp2\t0x%p\n", m); + + m = LNEXT(m); + } + + fprintf(f, "qp3\tTotal free messages : %d\n", n_free); + + fprintf(f, "qp4\t*** Active Messages ***\n"); + fprintf(f, "qp5\tAddress\tType\tSubtype\tuParam\tvParam\tnSent\tnCompleted\tnFailed\twait_o\trefcount\n"); + + m = msg_active; + while(m) { + + n_active++; + + fprintf(f, "qp6\t0x%p\t%d\t%d\t0x%x\t0x%p\t%d\t%d\t%d\t0x%p\t%d\n", + m, + (int) m->type, + (int) m->subtype, + (unsigned int) m->uparam, + m->vparam, + (int) m->nSent, + (int) m->nCompleted, + (int) m->nFailed, + (void *) m->wait_o, + (int) m->refcount); + + m = LNEXT(m); + } + + fprintf(f, "qp7\tTotal number of active messages = %d\n", n_active); + + fprintf(f, "qp8\t--- End ---\n"); + + LeaveCriticalSection(&cs_kmq_msg); + +} + +#endif + /*! \internal \brief Get a message object \note called with ::cs_kmq_msg held */ @@ -181,6 +241,12 @@ kmq_post_message_ex(khm_int32 type, khm_int32 subtype, return kmqint_post_message_ex(type, subtype, uparam, blob, call, FALSE); } +KHMEXP khm_int32 KHMAPI +kmq_abort_call(kmq_call call) +{ + /* TODO: Implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} /*! \internal */ @@ -445,15 +511,21 @@ kmq_post_thread_quit_message(kmq_thread_id thread, return KHM_ERROR_SUCCESS; } -/* TODO:Implement these */ KHMEXP khm_int32 KHMAPI kmq_get_next_response(kmq_call call, void ** resp) { + /* TODO: Implement this */ return 0; } KHMEXP khm_boolean KHMAPI kmq_has_completed(kmq_call call) { - return (call->nCompleted + call->nFailed == call->nSent); + khm_boolean completed; + + EnterCriticalSection(&cs_kmq_msg); + completed = (call->nCompleted + call->nFailed == call->nSent); + LeaveCriticalSection(&cs_kmq_msg); + + return completed; } KHMEXP khm_int32 KHMAPI @@ -481,3 +553,4 @@ kmq_set_completion_handler(khm_int32 type, return kmqint_msg_type_set_handler(type, handler); } + diff --git a/src/windows/identity/nidmgrdll/Makefile b/src/windows/identity/nidmgrdll/Makefile index d18ac510d..324097ef3 100644 --- a/src/windows/identity/nidmgrdll/Makefile +++ b/src/windows/identity/nidmgrdll/Makefile @@ -91,6 +91,7 @@ OBJFILES= \ $(UIDIR)\acceldef.obj \ $(UIDIR)\configui.obj \ $(UIDIR)\trackerwnd.obj \ + $(UIDIR)\uibind.obj \ $(UIDIR)\version.obj RESFILES= \ diff --git a/src/windows/identity/plugins/krb4/krb4newcreds.c b/src/windows/identity/plugins/krb4/krb4newcreds.c index 851f2e85a..81c16162d 100644 --- a/src/windows/identity/plugins/krb4/krb4newcreds.c +++ b/src/windows/identity/plugins/krb4/krb4newcreds.c @@ -853,6 +853,9 @@ krb4_msg_newcred(khm_int32 msg_type, khm_int32 msg_subtype, if (nct->name) PFREE(nct->name); + if (nct->credtext) + PFREE(nct->credtext); + PFREE(nct); } break; diff --git a/src/windows/identity/plugins/krb4/lang/en_us/langres.rc b/src/windows/identity/plugins/krb4/lang/en_us/langres.rc index 865f01039..374f6ced5 100644 --- a/src/windows/identity/plugins/krb4/lang/en_us/langres.rc +++ b/src/windows/identity/plugins/krb4/lang/en_us/langres.rc @@ -53,16 +53,16 @@ END // IDD_NC_KRB4 DIALOGEX 0, 0, 300, 166 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Kerberos v4 Ticket Options",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | SS_SUNKEN | WS_GROUP,7,7,286,11 - CONTROL "Obtain Kerberos v4 credentials",IDC_NCK4_OBTAIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,26,97,10 + CONTROL "Obtain Kerberos v4 credentials",IDC_NCK4_OBTAIN,"Button",BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,7,26,128,10 GROUPBOX "Obtain Kerberos v4 credentials using",IDC_STATIC,7,43,286,72,WS_GROUP - CONTROL "Automatically determine method",IDC_NCK4_AUTO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,23,58,119,10 - CONTROL "Kerberos v5 to v4 translation",IDC_NCK4_K524,"Button",BS_AUTORADIOBUTTON,23,76,101,10 - CONTROL "Password",IDC_NCK4_PWD,"Button",BS_AUTORADIOBUTTON,23,94,47,10 + CONTROL "Automatically determine method",IDC_NCK4_AUTO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,23,58,152,10 + CONTROL "Kerberos v5 to v4 translation",IDC_NCK4_K524,"Button",BS_AUTORADIOBUTTON,23,76,152,10 + CONTROL "Password",IDC_NCK4_PWD,"Button",BS_AUTORADIOBUTTON,23,94,152,10 END IDD_CFG_KRB4 DIALOGEX 0, 0, 255, 182 @@ -202,3 +202,4 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + diff --git a/src/windows/identity/plugins/krb5/krb5identpro.c b/src/windows/identity/plugins/krb5/krb5identpro.c index 43d6d3d9d..c036eb976 100644 --- a/src/windows/identity/plugins/krb5/krb5identpro.c +++ b/src/windows/identity/plugins/krb5/krb5identpro.c @@ -1533,6 +1533,8 @@ DWORD WINAPI k5_ccname_monitor_thread(LPVOID lpParameter) { wchar_t reg_ccname[KRB5_MAXCCH_CCNAME]; LONG l; + PDESCTHREAD(L"Krb5 CCName Monitor", L"Krb5"); + l = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\MIT\\kerberos5", 0, diff --git a/src/windows/identity/plugins/krb5/krb5newcreds.c b/src/windows/identity/plugins/krb5/krb5newcreds.c index 087d937f4..34cc8bd20 100644 --- a/src/windows/identity/plugins/krb5/krb5newcreds.c +++ b/src/windows/identity/plugins/krb5/krb5newcreds.c @@ -1459,6 +1459,24 @@ k5_write_dlg_params(khm_handle conf, d->dirty = FALSE; } +void +k5_free_kinit_job(void) +{ + if (g_fjob.principal) + PFREE(g_fjob.principal); + + if (g_fjob.password) + PFREE(g_fjob.password); + + if (g_fjob.identity) + kcdb_identity_release(g_fjob.identity); + + if (g_fjob.ccache) + PFREE(g_fjob.ccache); + + ZeroMemory(&g_fjob, sizeof(g_fjob)); +} + void k5_prep_kinit_job(khui_new_creds * nc) { @@ -1489,8 +1507,9 @@ k5_prep_kinit_job(khui_new_creds * nc) kcdb_identity_get_name(ident, idname, &cbbuf); StringCchLength(idname, ARRAYLENGTH(idname), &size); size++; - - ZeroMemory(&g_fjob, sizeof(g_fjob)); + + k5_free_kinit_job(); + g_fjob.command = FIBER_CMD_KINIT; g_fjob.nc = nc; g_fjob.nct = nct; @@ -1571,24 +1590,6 @@ k5_prep_kinit_job(khui_new_creds * nc) /* leave identity held, since we added a reference above */ } -void -k5_free_kinit_job(void) -{ - if (g_fjob.principal) - PFREE(g_fjob.principal); - - if (g_fjob.password) - PFREE(g_fjob.password); - - if (g_fjob.identity) - kcdb_identity_release(g_fjob.identity); - - if (g_fjob.ccache) - PFREE(g_fjob.ccache); - - ZeroMemory(&g_fjob, sizeof(g_fjob)); -} - static khm_int32 KHMAPI k5_find_tgt_filter(khm_handle cred, khm_int32 flags, @@ -2014,6 +2015,13 @@ k5_msg_cred_dialog(khm_int32 msg_type, if (d->pwd_change) return KHM_ERROR_SUCCESS; + /* At this point, we assume that the current set of + prompts is no longer valid. And since we might not be + able to come up with a set of prompts until the KDC + replies (unless we have cached prompts), we remove the + ones that are already shown. */ + khui_cw_clear_prompts(nc); + /* if the fiber is already in a kinit, cancel it */ if(g_fjob.state == FIBER_STATE_KINIT) { g_fjob.command = FIBER_CMD_CANCEL; @@ -2121,6 +2129,12 @@ k5_msg_cred_dialog(khm_int32 msg_type, k5_free_kinit_job(); + if (is_k5_identpro) + kcdb_identity_set_flags(ident, + KCDB_IDENT_FLAG_UNKNOWN, + KCDB_IDENT_FLAG_UNKNOWN); + + } else if(g_fjob.state == FIBER_STATE_KINIT) { /* this is what we want. Leave the fiber there. */ @@ -2790,6 +2804,8 @@ k5_msg_cred_dialog(khm_int32 msg_type, PFREE(nct->credtext); PFREE(nct); + + k5_free_kinit_job(); } break; diff --git a/src/windows/identity/ui/addrchange.c b/src/windows/identity/ui/addrchange.c index 3e5467ccc..b37fca534 100644 --- a/src/windows/identity/ui/addrchange.c +++ b/src/windows/identity/ui/addrchange.c @@ -39,6 +39,8 @@ addr_change_thread(LPVOID dummy) { OVERLAPPED overlap; DWORD ret; + PDESCTHREAD(L"Address change waiter", L"App"); + ZeroMemory(&overlap, sizeof(overlap)); h_notify = NULL; diff --git a/src/windows/identity/ui/appglobal.h b/src/windows/identity/ui/appglobal.h index 8660de2b4..b25d38247 100644 --- a/src/windows/identity/ui/appglobal.h +++ b/src/windows/identity/ui/appglobal.h @@ -40,14 +40,15 @@ extern khm_version app_version; #define IS_COMMCTL6() (khm_commctl_version >= 0x60000) -typedef struct tag_khm_startup_options_v1 { +/* The structure used to send command-line options to a remote + NetIDMgr session for versions prior to 1.2. */ +struct tag_khm_startup_options_v1 { BOOL seen; BOOL processing; BOOL init; BOOL import; BOOL renew; - LONG pending_renewals; BOOL destroy; BOOL autoinit; @@ -55,10 +56,51 @@ typedef struct tag_khm_startup_options_v1 { BOOL error_exit; BOOL no_main_window; +}; + +/* Used on NetIDMgr versions 1.2 and later */ +struct tag_khm_startup_options_v2 { + khm_int32 magic; + DWORD cb_size; + + BOOL init; + BOOL import; + BOOL renew; + BOOL destroy; + + BOOL autoinit; + BOOL remote_exit; + + khm_int32 code; +} khm_startup_options_xfer; + +#define STARTUP_OPTIONS_MAGIC 0x1f280e41 + +/* Used internally. */ +typedef struct tag_khm_startup_options_int { + BOOL seen; + BOOL processing; + BOOL remote; + + BOOL init; + BOOL import; + BOOL renew; + BOOL destroy; + + BOOL autoinit; + BOOL exit; + BOOL remote_exit; + + BOOL error_exit; + + BOOL no_main_window; + + LONG pending_renewals; } khm_startup_options; extern khm_startup_options khm_startup; +/* Used to query a remote instance of NetIDMgr for the version. */ typedef struct tag_khm_query_app_version_v1 { khm_int32 magic; @@ -94,6 +136,6 @@ WPARAM khm_message_loop_int(khm_boolean * p_exit); #define MAX_RES_STRING 1024 -#define ELIPSIS L"..." +#define ELLIPSIS L"..." #endif diff --git a/src/windows/identity/ui/cfg_identities_wnd.c b/src/windows/identity/ui/cfg_identities_wnd.c index 3f92ad839..3ba843f75 100644 --- a/src/windows/identity/ui/cfg_identities_wnd.c +++ b/src/windows/identity/ui/cfg_identities_wnd.c @@ -483,10 +483,7 @@ write_params_ident(ident_data * d) { static void write_params_idents(void) { -#if 0 - int i; -#endif - khm_handle csp_cw; + khm_handle csp_cw = NULL; if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", KHM_FLAG_CREATE, &csp_cw))) { @@ -508,7 +505,9 @@ write_params_idents(void) { cfg_idents.work.sticky = cfg_idents.saved.sticky; cfg_idents.applied = TRUE; } + khc_close_space(csp_cw); + csp_cw = NULL; } #if 0 @@ -530,7 +529,7 @@ init_idents_data(void) { khm_size cb; int n_tries = 0; int i; - khm_handle csp_cw; + khm_handle csp_cw = NULL; if (cfg_idents.valid) return; @@ -559,6 +558,9 @@ init_idents_data(void) { else cfg_idents.saved.sticky = FALSE; + khc_close_space(csp_cw); + csp_cw = NULL; + } else { cfg_idents.saved.monitor = TRUE; diff --git a/src/windows/identity/ui/configwnd.c b/src/windows/identity/ui/configwnd.c index 660c1fae8..03eec9ad5 100644 --- a/src/windows/identity/ui/configwnd.c +++ b/src/windows/identity/ui/configwnd.c @@ -784,10 +784,14 @@ cfgui_dlgproc(HWND hwnd, khui_delete_bitmap(&d->kbmp_logo); DeleteObject(d->hbr_white); + cfgui_set_wnd_data(hwnd, NULL); + khm_del_dialog(hwnd); SetForegroundWindow(khm_hwnd_main); + PFREE(d); + return FALSE; case WM_NOTIFY: @@ -1043,6 +1047,8 @@ void khm_refresh_config(void) { if (omenu == NULL) goto _cleanup; + khui_action_lock(); + do { khm_int32 action; khm_int32 flags; @@ -1099,8 +1105,11 @@ void khm_refresh_config(void) { break; } while(cfg_r); - if (refresh_menu) - khm_menu_refresh_items(); + khui_action_unlock(); + + if (refresh_menu) { + khui_refresh_actions(); + } _cleanup: if (cfg_ids) diff --git a/src/windows/identity/ui/credfuncs.c b/src/windows/identity/ui/credfuncs.c index e70b8526e..7b7032212 100644 --- a/src/windows/identity/ui/credfuncs.c +++ b/src/windows/identity/ui/credfuncs.c @@ -156,7 +156,11 @@ khm_cred_wait_for_dialog(DWORD timeout, khm_int32 * result, return rv; } -/* completion handler for KMSG_CRED messages */ +/* Completion handler for KMSG_CRED messages. We control the overall + logic of credentials acquisition and other operations here. Once a + credentials operation is triggered, each successive message + completion notification will be used to dispatch the messages for + the next step in processing the operation. */ void KHMAPI kmsg_cred_completion(kmq_message *m) { @@ -183,7 +187,7 @@ kmsg_cred_completion(kmq_message *m) nc = (khui_new_creds *) m->vparam; /* khm_cred_dispatch_process_message() deals with the case - where there are not credential types that wants to + where there are no credential types that wants to participate in this operation. */ khm_cred_dispatch_process_message(nc); break; @@ -281,6 +285,8 @@ kmsg_cred_completion(kmq_message *m) if (nc->subtype == KMSG_CRED_NEW_CREDS) { + khui_alert_set_type(alert, KHUI_ALERTTYPE_ACQUIREFAIL); + cb = sizeof(w_idname); if (nc->n_identities == 0 || KHM_FAILED(kcdb_identity_get_name(nc->identities[0], @@ -293,10 +299,17 @@ kmsg_cred_completion(kmq_message *m) ws_tfmt, ARRAYLENGTH(ws_tfmt)); StringCbPrintf(ws_title, sizeof(ws_title), ws_tfmt, w_idname); + khui_alert_set_ctx(alert, + KHUI_SCOPE_IDENT, + nc->identities[0], + KCDB_CREDTYPE_INVALID, + NULL); } } else if (nc->subtype == KMSG_CRED_PASSWORD) { + khui_alert_set_type(alert, KHUI_ALERTTYPE_CHPW); + cb = sizeof(w_idname); if (nc->n_identities == 0 || KHM_FAILED(kcdb_identity_get_name(nc->identities[0], @@ -308,10 +321,17 @@ kmsg_cred_completion(kmq_message *m) ws_tfmt, ARRAYLENGTH(ws_tfmt)); StringCbPrintf(ws_title, sizeof(ws_title), ws_tfmt, w_idname); + khui_alert_set_ctx(alert, + KHUI_SCOPE_IDENT, + nc->identities[0], + KCDB_CREDTYPE_INVALID, + NULL); } } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) { + khui_alert_set_type(alert, KHUI_ALERTTYPE_RENEWFAIL); + cb = sizeof(w_idname); if (nc->ctx.identity == NULL || KHM_FAILED(kcdb_identity_get_name(nc->ctx.identity, @@ -323,6 +343,11 @@ kmsg_cred_completion(kmq_message *m) ws_tfmt, ARRAYLENGTH(ws_tfmt)); StringCbPrintf(ws_title, sizeof(ws_title), ws_tfmt, w_idname); + khui_alert_set_ctx(alert, + KHUI_SCOPE_IDENT, + nc->ctx.identity, + KCDB_CREDTYPE_INVALID, + NULL); } } else { @@ -374,11 +399,6 @@ kmsg_cred_completion(kmq_message *m) if (nc->subtype == KMSG_CRED_NEW_CREDS || nc->subtype == KMSG_CRED_PASSWORD) { - /* - if (nc->subtype == KMSG_CRED_NEW_CREDS) - khui_context_reset(); - */ - khm_cred_end_dialog(nc); } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) { @@ -1169,6 +1189,7 @@ khm_cred_process_startup_actions(void) { /* when we get here, then we are all done with the command line stuff */ khm_startup.processing = FALSE; + khm_startup.remote = FALSE; } while(FALSE); if (defident) @@ -1182,7 +1203,9 @@ khm_cred_begin_startup_actions(void) { if (khm_startup.seen) return; - if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) { + if (!khm_startup.remote && + KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) { + khm_int32 t = 0; khc_read_int32(csp_cw, L"Autoinit", &t); diff --git a/src/windows/identity/ui/credwnd.c b/src/windows/identity/ui/credwnd.c index 5bdbdb754..a555ebeaa 100644 --- a/src/windows/identity/ui/credwnd.c +++ b/src/windows/identity/ui/credwnd.c @@ -4618,7 +4618,7 @@ khm_register_credwnd_class(void) { wcx.hInstance = khm_hInstance; wcx.hIcon = NULL; wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); - wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); + wcx.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wcx.lpszMenuName = NULL; wcx.lpszClassName = KHUI_CREDWND_CLASS_NAME; wcx.hIconSm = NULL; diff --git a/src/windows/identity/ui/khmapp.h b/src/windows/identity/ui/khmapp.h index 203f5b02b..6b79307a2 100644 --- a/src/windows/identity/ui/khmapp.h +++ b/src/windows/identity/ui/khmapp.h @@ -36,19 +36,14 @@ #define KHERR_HMODULE khm_hInstance #define KHERR_FACILITY khm_facility #define KHERR_FACILITY_ID 3 + #define NOEXPORT -#include -#include -#include -#include -#include -#include -#include -#include +#include + #include -#include -#include +#include +#include #include #include diff --git a/src/windows/identity/ui/lang/en_us/khapp.rc b/src/windows/identity/ui/lang/en_us/khapp.rc index 862872dae..6fadebb94 100644 --- a/src/windows/identity/ui/lang/en_us/khapp.rc +++ b/src/windows/identity/ui/lang/en_us/khapp.rc @@ -34,7 +34,7 @@ END 2 TEXTINCLUDE BEGIN - "#include ""afxres.h""\r\n" + "#include ""afxres.h""\r\0" END 3 TEXTINCLUDE @@ -174,28 +174,21 @@ BEGIN LTEXT "TplInput",IDC_NC_TPL_INPUT,54,7,240,13,NOT WS_VISIBLE | WS_BORDER LTEXT "TplLabelLg",IDC_NC_TPL_LABEL_LG,7,33,146,10,NOT WS_VISIBLE | WS_BORDER LTEXT "TplInputLg",IDC_NC_TPL_INPUT_LG,155,31,139,13,NOT WS_VISIBLE | WS_BORDER - LTEXT "&Credentials",IDC_NC_CREDTEXT_LABEL,7,66,41,10,NOT WS_GROUP - CONTROL "",IDC_NC_CREDTEXT,"KhmHtWnd",WS_TABSTOP,54,65,240,73,WS_EX_CLIENTEDGE - PUSHBUTTON "&Ok",IDOK,57,142,89,18,WS_DISABLED - PUSHBUTTON "&Cancel",IDCANCEL,158,142,54,18 - PUSHBUTTON "&Options >>",IDC_NC_OPTIONS,223,142,71,18 + LTEXT "&Credentials",IDC_NC_CREDTEXT_LABEL,7,66,41,10,NOT WS_VISIBLE | NOT WS_GROUP + CONTROL "",IDC_NC_CREDTEXT,"KhmHtWnd",NOT WS_VISIBLE | WS_TABSTOP,54,65,240,95,WS_EX_CLIENTEDGE + PUSHBUTTON "&Ok",IDOK,101,142,89,18,WS_DISABLED + PUSHBUTTON "&Cancel",IDCANCEL,198,142,54,18 + PUSHBUTTON "&>>",IDC_NC_ADVANCED,260,142,34,18 END -IDD_NC_BBAR DIALOGEX 0, 0, 60, 181 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU -EXSTYLE WS_EX_CONTROLPARENT -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "&Ok",IDOK,0,7,53,41,WS_DISABLED - PUSHBUTTON "&Cancel",IDCANCEL,0,58,53,19 - PUSHBUTTON "&Help",IDC_NC_HELP,0,155,53,19 -END - -IDD_NC_TS DIALOGEX 0, 0, 300, 15 +IDD_NC_BBAR DIALOGEX 0, 0, 66, 181 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN + DEFPUSHBUTTON "&Ok",IDOK,7,7,52,41,WS_DISABLED + PUSHBUTTON "&Cancel",IDCANCEL,7,58,52,19 + PUSHBUTTON "&Help",IDC_NC_HELP,7,155,52,19 END IDD_PP_IDENT DIALOGEX 0, 0, 235, 156 @@ -414,7 +407,8 @@ BEGIN IDD_NC_BBAR, DIALOG BEGIN - RIGHTMARGIN, 53 + LEFTMARGIN, 7 + RIGHTMARGIN, 59 TOPMARGIN, 7 BOTTOMMARGIN, 174 END @@ -560,7 +554,7 @@ STRINGTABLE BEGIN IDS_MENU_OPTIONS "&Options" IDS_MENU_HELP "&Help" - IDS_ACTION_PROPERTIES "&Properties ..." + IDS_ACTION_PROPERTIES "&Properties" IDS_ACTION_EXIT "E&xit" IDS_CFG_ROOT_NAME "Network Identity Manager" IDS_ACTION_SET_DEF_ID "Set as &default" @@ -568,9 +562,9 @@ BEGIN IDS_CFG_ROOT_TITLE "Network Identity Manager Configuration" IDS_CFG_GENERAL_SHORT "General" IDS_ACTION_NEW_CRED "&New credentials ..." - IDS_ACTION_PASSWD_ID "Change &password ..." + IDS_ACTION_PASSWD_ID "Change &password" IDS_ACTION_CHOOSE_COLS "View columns" - IDS_ACTION_DEBUG_WINDOW "Debug window ..." + IDS_ACTION_DEBUG_WINDOW "Debug window" IDS_ACTION_VIEW_REFRESH "Refresh view" IDS_MENU_LAYOUT "Layout" IDS_MENU_TOOLBARS "Toolbars" @@ -582,13 +576,13 @@ BEGIN IDS_ACTION_LAYOUT_TYPE "By type" IDS_ACTION_LAYOUT_LOC "By location" IDS_ACTION_TB_STANDARD "Standard" - IDS_ACTION_OPT_KHIM "General ..." - IDS_ACTION_OPT_IDENTS "Identities ..." - IDS_ACTION_OPT_NOTIF "Notifications ..." + IDS_ACTION_OPT_KHIM "General" + IDS_ACTION_OPT_IDENTS "Identities" + IDS_ACTION_OPT_NOTIF "Notifications" IDS_ACTION_HELP_CTX "Help Index" - IDS_ACTION_HELP_CONTENTS "Contents ..." - IDS_ACTION_HELP_INDEX "Index ..." - IDS_ACTION_HELP_ABOUT "About Network Identity Manager ..." + IDS_ACTION_HELP_CONTENTS "Contents" + IDS_ACTION_HELP_INDEX "Index" + IDS_ACTION_HELP_ABOUT "About Network Identity Manager" IDS_CFG_GENERAL_LONG "General options" IDS_SAMPLE_STRING "Wxy" IDS_NO_CREDS "
You currently have no credentials.Click here to obtain new credentials.
" @@ -607,7 +601,7 @@ BEGIN IDS_WTPOST_INIT_CREDS " - Initial credentials" IDS_WTPOST_NEW_CREDS " - New credentials" IDS_ACTION_RENEW_CRED "R&enew credentials" - IDS_ACTION_DESTROY_CRED "De&stroy credentials ..." + IDS_ACTION_DESTROY_CRED "De&stroy credentials" IDS_DEFAULT_FONT "MS Shell Dlg" IDS_NC_CREDTEXT_TABS "" IDS_NOTIFY_PREFIX "Network Identity Manager - " @@ -629,7 +623,7 @@ BEGIN IDS_PROP_COL_VALUE "Value" IDS_NC_NEW_IDENT "( New identity ... )" IDS_NC_CREDTEXT_ID_CHECKING "%s (Checking...)" - IDS_ACTION_OPEN_APP "Open Network Identity Manager ..." + IDS_ACTION_OPEN_APP "Open Network Identity Manager" IDS_CTX_NEW_IDENT "Obaining new identity" IDS_CTX_NEW_CREDS "Obtaining new credentials" IDS_CTX_RENEW_CREDS "Renewing credentials" @@ -688,11 +682,11 @@ BEGIN IDS_PISTATE_EXIT "Stopped" IDS_CTX_PASSWORD "Changing password" IDS_WT_PASSWORD "Changing password" - IDS_WTPOST_PASSWORD " - Changing password" + IDS_WTPOST_PASSWORD " - Changing password" IDS_CTX_PROC_PASSWORD "Changing password for %1!s!" IDS_NC_PWD_FAILED_TITLE "Failed to change password" - IDS_CMDLINE_HELP "Command line options for Network Identity Manager are :\n\n-a or --autoinit: Auto initialize credentials\n-i or --kinit: Obtain new credentials\n-d or --destroy: Destroy default identity\n-r or --renew: Renew all credentials" - IDS_PACTION_NEXT "Next alert..." + IDS_CMDLINE_HELP "Command line options for Network Identity Manager are :\n\n-a or --autoinit: Auto initialize credentials\n-i or --kinit: Obtain new credentials\n-d or --destroy: Destroy default identity\n-r or --renew: Renew all credentials\n-x or --exit: Exit the running instance of Network Identity Manager" + IDS_PACTION_NEXT "Next alert" IDS_ERR_TITLE_NO_IDENTPRO "Cannot proceed without identity provider" END @@ -703,7 +697,7 @@ BEGIN "This is quite possibly caused by the identity provider module failing to load properly." IDS_NC_REN_FAILED_TITLE "Failed to renew credentials" IDS_CW_DEFAULT "(Default)" - IDS_ACTION_OPT_PLUGINS "Plugins ..." + IDS_ACTION_OPT_PLUGINS "Plugins" IDS_NC_SETDEF "&Set as default identity" IDS_NC_ID_DEF "

This identity is the default

" IDS_NC_ID_WDEF "

Will be the default. (Don't make default)

" @@ -745,7 +739,7 @@ BEGIN IDS_APR_SAMPLE_TEXT_NORMAL "Sample text (normal). 01234567890" IDS_CFG_APPEAR_SHORT "Appearance" IDS_CFG_APPEAR_LONG "Appearance" - IDS_ACTION_OPT_APPEAR "Appearance ..." + IDS_ACTION_OPT_APPEAR "Appearance" IDS_APR_SAMPLE_TEXT_SEL "Sample text (selected). 01234567890" IDS_CFG_IDNAME_INV "The identity name %s is invalid." IDS_CFG_IDNAME_PRB "Can't add new identity %s" @@ -782,13 +776,18 @@ STRINGTABLE BEGIN IDS_NC_REN_FAILED_TITLE_I "Failed to renew creds for %s" IDS_CFG_IDNAME_NON "No identity selected. Please select an identity and try again." - IDS_MENU_DESTROY_CRED "Destroy ..." - IDS_MENU_RENEW_CRED "Renew ..." + IDS_MENU_DESTROY_CRED "Destroy" + IDS_MENU_RENEW_CRED "Renew" IDS_ACTION_DESTROY_ALL "All identities" IDS_ACTION_RENEW_ALL "All identities" IDS_IDACTION_RENEW "Renew credentials for %s" IDS_IDACTION_DESTROY "Destroy credentials for %s" IDS_CTX_DESTROY_ID "Destroying identity %1!s!" + IDS_NCN_IDENT_INVALID "Identity %s is invalid." + IDS_NCN_IDENT_CHECKING "Checking identity %s ..." + IDS_NCN_IDENT_UNKNOWN "Validity of identity %s coudn't be determined." + IDS_REMOTE_FAIL "The instance of Network Identity Manager that is already running is not responding to the remote request properly. Please check if you are running the latest version of Network Identity Manger software included with MIT Kerberos for Windows." + IDS_REMOTE_FAIL_TITLE "Failed to communicate with Network Identity Manager" END #endif // English (U.S.) resources @@ -806,3 +805,4 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + diff --git a/src/windows/identity/ui/main.c b/src/windows/identity/ui/main.c index c05fa6b7f..37ebe125f 100644 --- a/src/windows/identity/ui/main.c +++ b/src/windows/identity/ui/main.c @@ -30,6 +30,12 @@ #if DEBUG #include + +#if defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL) +/* needed for writing out leaked allocation and handle report */ +#include +#endif + #endif HINSTANCE khm_hInstance; @@ -106,6 +112,13 @@ void khm_parse_commandline(void) { !wcscmp(wargs[i], L"-autoinit")) { khm_startup.autoinit = TRUE; } + else if (!wcscmp(wargs[i], L"-x") || + !wcscmp(wargs[i], L"--exit") || + !wcscmp(wargs[i], L"-exit")) { + khm_startup.exit = TRUE; + khm_startup.remote_exit = TRUE; + khm_startup.no_main_window = TRUE; + } else { wchar_t help[2048]; @@ -122,7 +135,8 @@ void khm_parse_commandline(void) { /* special: always enable renew when other options aren't specified */ if (!khm_startup.exit && !khm_startup.destroy && - !khm_startup.init) + !khm_startup.init && + !khm_startup.remote_exit) khm_startup.renew = TRUE; } @@ -283,17 +297,25 @@ void khm_leave_modal(void) { } else { + HWND last_dialog = NULL; + /* we are exiting a modal loop. */ for (i=0; i < n_khui_dialogs; i++) { if(khui_dialogs[i].hwnd != khui_modal_dialog) { EnableWindow(khui_dialogs[i].hwnd, khui_dialogs[i].active); + last_dialog = khui_dialogs[i].hwnd; } } EnableWindow(khm_hwnd_main, TRUE); khui_modal_dialog = NULL; + + if(last_dialog) + SetActiveWindow(last_dialog); + else + SetActiveWindow(khm_hwnd_main); } } @@ -502,6 +524,20 @@ void khm_load_default_modules(void) { kmm_load_default_modules(); } +int version_compare(const khm_version * v1, const khm_version * v2) { + + if (v1->major != v2->major) + return ((int)v1->major) - ((int)v2->major); + + if (v1->minor != v2->minor) + return ((int)v1->minor) - ((int)v2->minor); + + if (v1->patch != v2->patch) + return ((int)v1->patch) - ((int)v2->patch); + + return ((int)v1->aux - ((int)v2->aux)); +} + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, @@ -510,6 +546,7 @@ int WINAPI WinMain(HINSTANCE hInstance, int rv = 0; HANDLE h_appmutex; BOOL slave = FALSE; + int mutex_retries = 5; khm_hInstance = hInstance; khm_nCmdShow = nCmdShow; @@ -519,6 +556,11 @@ int WINAPI WinMain(HINSTANCE hInstance, if (khm_startup.error_exit) return 0; + _retry_mutex: + + if (--mutex_retries < 0) + return 2; + h_appmutex = CreateMutex(NULL, FALSE, L"Local\\NetIDMgr_GlobalAppMutex"); if (h_appmutex == NULL) return 5; @@ -531,6 +573,8 @@ int WINAPI WinMain(HINSTANCE hInstance, if(!slave) { + PDESCTHREAD(L"UI", L"App"); + /* set this so that we don't accidently invoke an API that inadvertently puts up the new creds dialog at an inopportune moment, like, say, during the new creds dialog @@ -545,8 +589,8 @@ int WINAPI WinMain(HINSTANCE hInstance, /* we only open a main window if this is the only instance of the application that is running. */ kmq_init(); - kmm_init(); khm_init_gui(); + kmm_init(); kmq_set_completion_handler(KMSG_CRED, kmsg_cred_completion); @@ -574,9 +618,9 @@ int WINAPI WinMain(HINSTANCE hInstance, khm_exit_request_daemon(); + kmm_exit(); khm_exit_gui(); khm_unregister_window_classes(); - kmm_exit(); kmq_exit(); CloseHandle(h_appmutex); @@ -588,6 +632,9 @@ int WINAPI WinMain(HINSTANCE hInstance, DWORD tid; void * xfer; khm_query_app_version query_app_version; + khm_version v; + BOOL use_cmd_v2 = TRUE; + khm_size cb = 0; CloseHandle(h_appmutex); @@ -598,11 +645,21 @@ int WINAPI WinMain(HINSTANCE hInstance, break; retries--; + + /* if the app was just starting, we might have to wait + till the main window is created. */ + Sleep(1000); } - if (!hwnd) - return 2; + if (!hwnd) { + + /* if the app was just exiting, we might see the mutex but + not the window. We go back and check if the mutex is + still there. */ + + goto _retry_mutex; + } /* first check if the remote instance supports a version query */ @@ -647,6 +704,31 @@ int WINAPI WinMain(HINSTANCE hInstance, CloseHandle(hmap); hmap = NULL; + if (query_app_version.magic != KHM_QUERY_APP_VER_MAGIC || + query_app_version.code != KHM_ERROR_SUCCESS) { + + /* We managed to communicate with the remote instance, but + it didn't send us useful information. The remote + instance is not running an actual NetIDMgr instance. + However, it owns a top level window that was registered + with our classname. This instance won't function + properly if we let it proceed. + */ + + wchar_t error_msg[1024]; + wchar_t error_title[256]; + + LoadString(khm_hInstance, IDS_REMOTE_FAIL_TITLE, + error_title, ARRAYLENGTH(error_title)); + LoadString(khm_hInstance, IDS_REMOTE_FAIL, + error_msg, ARRAYLENGTH(error_msg)); + + MessageBox(NULL, error_msg, error_title, + MB_OK); + + goto done_with_remote; + } + if (query_app_version.code == KHM_ERROR_SUCCESS && query_app_version.request_swap) { /* the request for swap was granted. We can now @@ -656,43 +738,149 @@ int WINAPI WinMain(HINSTANCE hInstance, goto _start_app; } + /* Now we can work on sending the command-line to the remote + instance. However we need to figure out which version of + the startup structure it supports. */ + v.major = 1; + v.minor = 2; + v.patch = 0; + v.aux = 0; + + if (version_compare(&query_app_version.ver_remote, &app_version) == 0 || + version_compare(&query_app_version.ver_remote, &v) > 0) + use_cmd_v2 = TRUE; + else + use_cmd_v2 = FALSE; + StringCbPrintf(mapname, sizeof(mapname), COMMANDLINE_MAP_FMT, (tid = GetCurrentThreadId())); + cb = max(sizeof(struct tag_khm_startup_options_v1), + sizeof(struct tag_khm_startup_options_v2)); + + cb = UBOUNDSS(cb, 4096, 4096); + +#ifdef DEBUG + assert(cb >= 4096); +#endif + hmap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, - 4096, + (DWORD) cb, mapname); if (hmap == NULL) return 3; - xfer = MapViewOfFile(hmap, - FILE_MAP_WRITE, - 0, 0, - sizeof(khm_startup)); + /* make the call */ - if (xfer) { - memcpy(xfer, &khm_startup, sizeof(khm_startup)); + if (use_cmd_v2) { + /* use the v2 structure */ + struct tag_khm_startup_options_v2 v2opt; - SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE, - 0, (LPARAM) tid); + ZeroMemory(&v2opt, sizeof(v2opt)); + + v2opt.magic = STARTUP_OPTIONS_MAGIC; + v2opt.cb_size = sizeof(v2opt); + + v2opt.init = khm_startup.init; + v2opt.import = khm_startup.import; + v2opt.renew = khm_startup.renew; + v2opt.destroy = khm_startup.destroy; + + v2opt.autoinit = khm_startup.autoinit; + v2opt.remote_exit = khm_startup.remote_exit; + + v2opt.code = KHM_ERROR_NOT_IMPLEMENTED; + + xfer = MapViewOfFile(hmap, + FILE_MAP_WRITE, + 0, 0, + sizeof(v2opt)); + + if (xfer) { + memcpy(xfer, &v2opt, sizeof(v2opt)); + + SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V2, + 0, (LPARAM) tid); + + memcpy(&v2opt, xfer, sizeof(v2opt)); + + /* If the request looks like it wasn't processed, we + fallback to the v1 request. */ + + if (v2opt.code == KHM_ERROR_NOT_IMPLEMENTED) + use_cmd_v2 = FALSE; + + UnmapViewOfFile(xfer); + xfer = NULL; + } } - if (xfer) - UnmapViewOfFile(xfer); + if (!use_cmd_v2) { + /* use the v1 structure */ + + struct tag_khm_startup_options_v1 v1opt; + + ZeroMemory(&v1opt, sizeof(v1opt)); + + v1opt.init = khm_startup.init; + v1opt.import = khm_startup.import; + v1opt.renew = khm_startup.renew; + v1opt.destroy = khm_startup.destroy; + v1opt.autoinit = khm_startup.autoinit; + + xfer = MapViewOfFile(hmap, + FILE_MAP_WRITE, + 0, 0, + sizeof(v1opt)); + + if (xfer) { + memcpy(xfer, &v1opt, sizeof(v1opt)); + + SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V1, + 0, (LPARAM) tid); + + UnmapViewOfFile(xfer); + xfer = NULL; + } + } + + done_with_remote: if (hmap) CloseHandle(hmap); } -#if defined(DEBUG) && ( defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL)) - /* writes a report of memory leaks to the specified file. Should - only be enabled on development versions. */ - PDUMP("memleak.txt"); +#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL)) + { + FILE * f = NULL; + + KHMEXP void KHMAPI khcint_dump_handles(FILE * f); + KHMEXP void KHMAPI perf_dump(FILE * f); + KHMEXP void KHMAPI kmqint_dump(FILE * f); + +#if _MSC_VER >= 1400 + if (fopen_s(&f, "memleak.txt", "w") != 0) + goto done_with_dump; +#else + f = fopen("memleak.txt", "w"); + if (f == NULL) + goto done_with_dump; +#endif + + perf_dump(f); + khcint_dump_handles(f); + kmqint_dump(f); + + fclose(f); + + done_with_dump: + ; + } #endif return rv; diff --git a/src/windows/identity/ui/mainmenu.c b/src/windows/identity/ui/mainmenu.c index c99cafafd..e4267035e 100644 --- a/src/windows/identity/ui/mainmenu.c +++ b/src/windows/identity/ui/mainmenu.c @@ -166,12 +166,7 @@ static void refresh_menu_item(HMENU hm, khui_action * act, to add it. */ mii.fMask = MIIM_STATE; if (!GetMenuItemInfo(hm, act->cmd, FALSE, &mii)) { - /* the 1000 is fairly arbitrary, but there should be much - less menu items on a menu anyway. If there are that - many items, the system would be unusable to the extent - that the order of the items would be the least of our - worries. */ - add_action_to_menu(hm, act, 1000, flags); + add_action_to_menu(hm, act, idx, flags); return; } @@ -197,6 +192,7 @@ static void refresh_menu_item(HMENU hm, khui_action * act, SetMenuItemInfo(hm, act->cmd, FALSE, &mii); def = khui_find_menu(act->cmd); + if(def) { MENUITEMINFO mii2; diff --git a/src/windows/identity/ui/mainwnd.c b/src/windows/identity/ui/mainwnd.c index 71e09dfac..05e615786 100644 --- a/src/windows/identity/ui/mainwnd.c +++ b/src/windows/identity/ui/mainwnd.c @@ -220,6 +220,7 @@ khm_main_wnd_proc(HWND hwnd, khm_pre_shutdown(); kmq_unsubscribe_hwnd(KMSG_ACT, hwnd); kmq_unsubscribe_hwnd(KMSG_CRED, hwnd); + kmq_unsubscribe_hwnd(KMSG_KMM, hwnd); HtmlHelp(NULL, NULL, HH_CLOSE_ALL, 0); PostQuitMessage(0); break; @@ -286,15 +287,15 @@ khm_main_wnd_proc(HWND hwnd, case KHUI_ACTION_EXIT: DestroyWindow(hwnd); - break; + return 0; case KHUI_ACTION_OPEN_APP: khm_show_main_window(); - break; + return 0; case KHUI_ACTION_CLOSE_APP: khm_hide_main_window(); - break; + return 0; case KHUI_ACTION_OPT_KHIM: { khui_config_node node = NULL; @@ -302,7 +303,7 @@ khm_main_wnd_proc(HWND hwnd, khui_cfg_open(NULL, L"KhmGeneral", &node); khm_show_config_pane(node); } - break; + return 0; case KHUI_ACTION_OPT_IDENTS: { khui_config_node node = NULL; @@ -310,7 +311,7 @@ khm_main_wnd_proc(HWND hwnd, khui_cfg_open(NULL, L"KhmIdentities", &node); khm_show_config_pane(node); } - break; + return 0; case KHUI_ACTION_OPT_APPEAR: { khui_config_node node = NULL; @@ -318,7 +319,7 @@ khm_main_wnd_proc(HWND hwnd, khui_cfg_open(NULL, L"KhmAppear", &node); khm_show_config_pane(node); } - break; + return 0; case KHUI_ACTION_OPT_NOTIF: { khui_config_node node = NULL; @@ -326,7 +327,7 @@ khm_main_wnd_proc(HWND hwnd, khui_cfg_open(NULL, L"KhmNotifications", &node); khm_show_config_pane(node); } - break; + return 0; case KHUI_ACTION_OPT_PLUGINS: { khui_config_node node = NULL; @@ -334,27 +335,27 @@ khm_main_wnd_proc(HWND hwnd, khui_cfg_open(NULL, L"KhmPlugins", &node); khm_show_config_pane(node); } - break; + return 0; case KHUI_ACTION_HELP_CTX: khm_html_help(khm_hwnd_main, NULL, HH_HELP_CONTEXT, IDH_WELCOME); - break; + return 0; case KHUI_ACTION_HELP_CONTENTS: khm_html_help(khm_hwnd_main, NULL, HH_DISPLAY_TOC, 0); - break; + return 0; case KHUI_ACTION_HELP_INDEX: khm_html_help(khm_hwnd_main, NULL, HH_DISPLAY_INDEX, (DWORD_PTR) L""); - break; + return 0; case KHUI_ACTION_HELP_ABOUT: khm_create_about_window(); - break; + return 0; case KHUI_ACTION_IMPORT: khm_cred_import(); - break; + return 0; case KHUI_ACTION_PROPERTIES: /* properties are not handled by the main window. @@ -365,7 +366,7 @@ khm_main_wnd_proc(HWND hwnd, case KHUI_ACTION_UICB: khm_ui_cb(lParam); - break; + return 0; /* menu commands */ case KHUI_PACTION_MENU: @@ -445,6 +446,7 @@ khm_main_wnd_proc(HWND hwnd, act = khui_find_action(LOWORD(wParam)); if (act && act->listener) { kmq_post_sub_msg(act->listener, KMSG_ACT, KMSG_ACT_ACTIVATE, act->cmd, NULL); + return 0; } } } @@ -518,6 +520,8 @@ khm_main_wnd_proc(HWND hwnd, MW_RESIZE_TIMER, MW_RESIZE_TIMEOUT, NULL); + + return 0; } break; @@ -550,14 +554,19 @@ khm_main_wnd_proc(HWND hwnd, } khc_close_space(csp_cw); } + + return 0; + } else if (wParam == MW_REFRESH_TIMER) { kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0); + + return 0; + } break; case WM_MENUSELECT: return khm_menu_handle_select(wParam, lParam); - break; case KMQ_WM_DISPATCH: { @@ -611,13 +620,15 @@ khm_main_wnd_proc(HWND hwnd, } return kmq_wm_end(m, rv); } - break; + return 0; - case WM_KHUI_ASSIGN_COMMANDLINE: + case WM_KHUI_ASSIGN_COMMANDLINE_V1: { HANDLE hmap; void * xfer; wchar_t mapname[256]; + struct tag_khm_startup_options_v1 * pv1opt; + int code = KHM_ERROR_SUCCESS; StringCbPrintf(mapname, sizeof(mapname), COMMANDLINE_MAP_FMT, (DWORD) lParam); @@ -628,27 +639,110 @@ khm_main_wnd_proc(HWND hwnd, return 1; xfer = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, - sizeof(khm_startup)); + sizeof(*pv1opt)); if (xfer) { - memcpy(&khm_startup, xfer, sizeof(khm_startup)); + pv1opt = (struct tag_khm_startup_options_v1 *) xfer; + + khm_startup.init = pv1opt->init; + khm_startup.import = pv1opt->import; + khm_startup.renew = pv1opt->renew; + khm_startup.destroy = pv1opt->destroy; + + khm_startup.autoinit = pv1opt->autoinit; + khm_startup.error_exit = FALSE; + + khm_startup.no_main_window = FALSE; + khm_startup.remote_exit = FALSE; UnmapViewOfFile(xfer); + } else { + code = KHM_ERROR_NOT_FOUND; } CloseHandle(hmap); if(InSendMessage()) - ReplyMessage(0); + ReplyMessage(code); - khm_startup.exit = FALSE; + if (code == KHM_ERROR_SUCCESS) { + khm_startup.exit = FALSE; - khm_startup.seen = FALSE; - khm_startup.processing = FALSE; + khm_startup.seen = FALSE; + khm_startup.remote = TRUE; +#ifdef DEBUG + assert(!khm_startup.processing); +#endif + khm_startup.processing = FALSE; - khm_cred_begin_startup_actions(); + khm_cred_begin_startup_actions(); + } + + return code; + } + + case WM_KHUI_ASSIGN_COMMANDLINE_V2: + { + HANDLE hmap; + void * xfer; + wchar_t mapname[256]; + struct tag_khm_startup_options_v2 *pv2opt; + int code = KHM_ERROR_SUCCESS; + + StringCbPrintf(mapname, sizeof(mapname), + COMMANDLINE_MAP_FMT, (DWORD) lParam); + + hmap = OpenFileMapping(FILE_MAP_WRITE, FALSE, mapname); + + if (hmap == NULL) + return 1; + + xfer = MapViewOfFile(hmap, FILE_MAP_WRITE, 0, 0, + sizeof(*pv2opt)); + + if (xfer) { + pv2opt = (struct tag_khm_startup_options_v2 *) xfer; + + if (pv2opt->magic != STARTUP_OPTIONS_MAGIC || + pv2opt->cb_size != sizeof(*pv2opt)) { + code = KHM_ERROR_INVALID_PARAM; + goto done_with_v2_opt; + } + + khm_startup.init = pv2opt->init; + khm_startup.import = pv2opt->import; + khm_startup.renew = pv2opt->renew; + khm_startup.destroy = pv2opt->destroy; + + khm_startup.autoinit = pv2opt->autoinit; + khm_startup.exit = pv2opt->remote_exit; + + pv2opt->code = KHM_ERROR_SUCCESS; + + done_with_v2_opt: + UnmapViewOfFile(xfer); + } else { + code = KHM_ERROR_NOT_FOUND; + } + + CloseHandle(hmap); + + if(InSendMessage()) + ReplyMessage(code); + + if (code == KHM_ERROR_SUCCESS) { + khm_startup.seen = FALSE; + khm_startup.remote = TRUE; +#ifdef DEBUG + assert(!khm_startup.processing); +#endif + khm_startup.processing = FALSE; + + khm_cred_begin_startup_actions(); + } + + return code; } - break; case WM_KHUI_QUERY_APP_VERSION: { @@ -676,7 +770,7 @@ khm_main_wnd_proc(HWND hwnd, CloseHandle(hmap); } - break; + return 0; } return DefWindowProc(hwnd,uMsg,wParam,lParam); @@ -765,12 +859,80 @@ khm_create_main_window_controls(HWND hwnd_main) { khm_hwnd_main_cred = khm_create_credwnd(hwnd_main); } +void +khm_adjust_window_dimensions_for_display(RECT * pr) { + + HMONITOR hmon; + RECT rm; + long x, y, width, height; + + x = pr->left; + y = pr->top; + width = pr->right - pr->left; + height = pr->bottom - pr->top; + + /* if the rect doesn't intersect with the display area of any + monitor, we just default to the primary monitor. */ + hmon = MonitorFromRect(pr, MONITOR_DEFAULTTOPRIMARY); + + if (hmon == NULL) { + /* huh? we'll just center this on the primary screen */ + goto nomonitor; + } else { + MONITORINFO mi; + + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (!GetMonitorInfo(hmon, &mi)) + goto nomonitor; + + CopyRect(&rm, &mi.rcWork); + + goto adjust_dims; + } + + nomonitor: + /* for some reason we couldn't get a handle on a monitor or we + couldn't get the metrics for that monitor. We default to + setting things up on the primary monitor. */ + + SetRectEmpty(&rm); + if (!SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID) &rm, 0)) + goto done_with_monitor; + + adjust_dims: + + if (width > (rm.right - rm.left)) + width = rm.right - rm.left; + if (height > (rm.bottom - rm.top)) + height = rm.bottom - rm.top; + + if (x < rm.left) + x = rm.left; + if (x + width > rm.right) + x = rm.right - width; + if (y < rm.top) + y = rm.top; + if (y + height > rm.bottom) + y = rm.bottom - height; + + done_with_monitor: + pr->left = x; + pr->top = y; + pr->right = x + width; + pr->bottom = y + height; + +} + + void khm_create_main_window(void) { wchar_t buf[1024]; khm_handle csp_cw = NULL; khm_handle csp_mw = NULL; int x,y,width,height; + RECT r; LoadString(khm_hInstance, IDS_MAIN_WINDOW_TITLE, buf, ARRAYLENGTH(buf)); @@ -817,13 +979,23 @@ khm_create_main_window(void) { khc_close_space(csp_cw); } + /* The saved dimensions might not actually be visible if the user + has changed the resolution of the display or if it's a multiple + monitor system where the monitor on which the Network Identity + Manager window was on previously is no longer connected. We + have to check for that and adjust the dimensions if needed. */ + SetRect(&r, x, y, x + width, y + height); + khm_adjust_window_dimensions_for_display(&r); + khm_hwnd_main = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, MAKEINTATOM(khm_main_window_class), buf, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, - x, y, width, height, + r.left, r.top, + r.right - r.left, + r.bottom - r.top, khm_hwnd_null, NULL, NULL, diff --git a/src/windows/identity/ui/mainwnd.h b/src/windows/identity/ui/mainwnd.h index 5d0bdc564..221bf4246 100644 --- a/src/windows/identity/ui/mainwnd.h +++ b/src/windows/identity/ui/mainwnd.h @@ -55,8 +55,9 @@ khm_main_wnd_proc(HWND hwnd, WPARAM wParam, LPARAM lParam); -#define WM_KHUI_ASSIGN_COMMANDLINE 32808 #define WM_KHUI_QUERY_APP_VERSION 32809 +#define WM_KHUI_ASSIGN_COMMANDLINE_V1 32808 +#define WM_KHUI_ASSIGN_COMMANDLINE_V2 32810 #define COMMANDLINE_MAP_FMT L"Local\\NetIDMgr_Cmdline_%lu" #define QUERY_APP_VER_MAP_FMT L"Local\\NetIDMgr_QueryVer_%lu" diff --git a/src/windows/identity/ui/newcredwnd.c b/src/windows/identity/ui/newcredwnd.c index 75ebef4d8..8265aff75 100644 --- a/src/windows/identity/ui/newcredwnd.c +++ b/src/windows/identity/ui/newcredwnd.c @@ -25,6 +25,10 @@ /* $Id$ */ +/* Include the OEMRESOURCE constants for locating standard icon + resources. */ +#define OEMRESOURCE + #include #include @@ -34,8 +38,15 @@ ATOM khui_newcredwnd_cls; static void nc_position_credtext(khui_nc_wnd_data * d); -/* Common dialog procedure. Be careful. This is used by more than - one dialog. */ +/* Common dialog procedure used by the main credential panel + (IDD_NC_NEWCRED) and the button bar (IDC_NC_BBAR). */ + +static void +nc_layout_main_panel(khui_nc_wnd_data * d); + +static void +nc_layout_new_cred_window(khui_nc_wnd_data * d); + static INT_PTR CALLBACK nc_common_dlg_proc(HWND hwnd, UINT uMsg, @@ -53,8 +64,9 @@ nc_common_dlg_proc(HWND hwnd, #pragma warning(disable: 4244) SetWindowLongPtr(hwnd, DWLP_USER, lParam); #pragma warning(pop) + if (d->nc->subtype == KMSG_CRED_PASSWORD) { - ShowWindow(GetDlgItem(hwnd, IDC_NC_OPTIONS), + ShowWindow(GetDlgItem(hwnd, IDC_NC_ADVANCED), SW_HIDE); } } @@ -76,28 +88,6 @@ nc_common_dlg_proc(HWND hwnd, } break; -#if 0 - /* someday this will be used to draw custom tab buttons. But - that's not today */ - case WM_DRAWITEM: - { - khui_nc_wnd_data * d; - int id; - LPDRAWITEMSTRUCT ds; - - d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); - id = wParam; - ds = (LPDRAWITEMSTRUCT) lParam; - - if(id >= NC_TS_CTRL_ID_MIN && id <= NC_TS_CTRL_ID_MAX) { - /*TODO: custom draw the buttons */ - } - else - return FALSE; - } - break; -#endif - case KHUI_WM_NC_NOTIFY: { khui_nc_wnd_data * d; @@ -107,22 +97,15 @@ nc_common_dlg_proc(HWND hwnd, /* message sent by parent to notify us of something */ switch(HIWORD(wParam)) { case WMNC_DIALOG_EXPAND: + /* fallthrough */ + case WMNC_UPDATE_LAYOUT: if(hwnd == d->dlg_main) { - HWND hw; - - if(hw = GetDlgItem(hwnd, IDOK)) - ShowWindow(hw, SW_HIDE); - if(hw = GetDlgItem(hwnd, IDCANCEL)) - ShowWindow(hw, SW_HIDE); - if(hw = GetDlgItem(hwnd, IDC_NC_OPTIONS)) - ShowWindow(hw, SW_HIDE); - d->r_credtext.bottom = d->r_area.bottom; - - nc_position_credtext(d); + nc_layout_main_panel(d); return TRUE; } + break; /* nop */ } } return TRUE; @@ -136,6 +119,9 @@ nc_common_dlg_proc(HWND hwnd, d = (khui_nc_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + /* TODO: filter out and forward only the messages that + originated or pertain to the identity selection + controls. */ if (d && d->nc && d->nc->ident_cb) { return d->nc->ident_cb(d->nc, WMNC_IDENT_WMSG, hwnd, uMsg, wParam, lParam); @@ -146,51 +132,580 @@ nc_common_dlg_proc(HWND hwnd, } static void -nc_position_credtext(khui_nc_wnd_data * d) -{ - HWND hw; +nc_notify_clear(khui_nc_wnd_data * d) { + + if (d->notif_type == NC_NOTIFY_NONE) + /* there are no notifications anyway. */ + return; + + if (d->hwnd_notif_label) + DestroyWindow(d->hwnd_notif_label); + + if (d->hwnd_notif_aux) + DestroyWindow(d->hwnd_notif_aux); + + d->hwnd_notif_label = NULL; + d->hwnd_notif_aux = NULL; + + SetRectEmpty(&d->r_notif); + + d->notif_type = NC_NOTIFY_NONE; + + /* Note that we must call nc_layout_main_panel() after calling + this to adjust the layout of the main panel. However we aren't + calling it here since we might want to add another set of + notifications or make other changes to the main panel content + before calling nc_layout_main_panel(). */ +} + +static void +nc_notify_marquee(khui_nc_wnd_data * d, const wchar_t * label) { + +#if (_WIN32_IE >= 0x0600) + HDC hdc; + size_t length; + SIZE label_size; +#endif + + RECT r_label; + RECT r_mq; + RECT r_row; + HFONT hfont; + HWND hwnd; + HDWP hdefer; + + /* Clear the notification area. We only support one notification + at a time. */ + nc_notify_clear(d); - hw = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT); #ifdef DEBUG - assert(hw); + assert(d->dlg_main); #endif - if (d->r_credtext.bottom < d->r_credtext.top + d->r_row.bottom * 2) { - /* not enough room */ - if (d->nc->mode == KHUI_NC_MODE_MINI && - d->nc->subtype != KMSG_CRED_PASSWORD) { - PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY, - MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0); - return; +#if (_WIN32_IE >= 0x0600) + + /* We can only show the marquee control if the comctl32 DLL is + version 6.0 or later. Otherwise we only show the label. */ + + if (FAILED(StringCchLength(label, KHUI_MAXCCH_SHORT_DESC, &length))) { +#ifdef DEBUG + assert(FALSE); +#endif + length = KHUI_MAXCCH_SHORT_DESC; + } + + /* See how big the notification control needs to be. */ + + hdc = GetDC(d->dlg_main); +#ifdef DEBUG + assert(hdc != NULL); +#endif + + GetTextExtentPoint32(hdc, label, (int) length, &label_size); + + ReleaseDC(d->dlg_main, hdc); + + CopyRect(&r_row, &d->r_row); + + if (label_size.cx > d->r_e_label.right - d->r_e_label.left) { + /* using an entire row */ + CopyRect(&r_label, &d->r_row); + CopyRect(&r_mq, &d->r_n_input); + OffsetRect(&r_mq, 0, r_row.bottom - r_row.top); + r_row.bottom += r_row.bottom - r_row.top; + } else if (label_size.cx > d->r_n_label.right - d->r_n_label.left) { + /* using large labels */ + CopyRect(&r_label, &d->r_e_label); + CopyRect(&r_mq, &d->r_e_input); + } else { + /* normal labels */ + CopyRect(&r_label, &d->r_n_label); + CopyRect(&r_mq, &d->r_n_input); + } + + InflateRect(&r_mq, 0, - ((r_mq.bottom - r_mq.top) / 4)); + +#else /* _WIN32_IE < 0x0600 */ + + /* We are just showing the label */ + CopyRect(&r_row, &d->r_row); + CopyRect(&r_label, &r_row); + SetRectEmpty(&r_mq); + +#endif /* _WIN32_IE >= 0x0600 */ + + { + long y; + + if (IsRectEmpty(&d->r_custprompt)) { + y = d->r_idspec.bottom; } else { - ShowWindow(hw, SW_HIDE); - return; + y = d->r_custprompt.bottom; } + + OffsetRect(&r_row, d->r_area.left, y); + OffsetRect(&r_label, r_row.left, r_row.top); + OffsetRect(&r_mq, r_row.left, r_row.top); + } + + hfont = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); + + hdefer = BeginDeferWindowPos(2); + + /* the label */ + hwnd = CreateWindowEx(0, + L"STATIC", + label, + WS_CHILD | SS_ENDELLIPSIS, + r_label.left, r_label.top, + r_label.right - r_label.left, + r_label.bottom - r_label.top, + d->dlg_main, + NULL, NULL, NULL); +#ifdef DEBUG + assert(hwnd != NULL); +#endif + SendMessage(hwnd, WM_SETFONT, (WPARAM) hfont, (LPARAM) TRUE); + + DeferWindowPos(hdefer, hwnd, NULL, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSIZE | SWP_SHOWWINDOW); + + d->hwnd_notif_label = hwnd; + + /* and the marquee */ + +#if (_WIN32_IE >= 0x0600) + + /* unfortunately, the marquee is only available on comctl32 + version 6.0 or later. On previous versions, we only display + the message label. */ + + hwnd = CreateWindowEx(0, + PROGRESS_CLASS, + L"", + WS_CHILD | PBS_MARQUEE, + r_mq.left, r_mq.top, + r_mq.right - r_mq.left, + r_mq.bottom - r_mq.top, + d->dlg_main, + NULL, NULL, NULL); +#ifdef DEBUG + assert(hwnd != NULL); +#endif + + SendMessage(hwnd, PBM_SETMARQUEE, TRUE, 100); + + DeferWindowPos(hdefer, hwnd, NULL, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSIZE | SWP_SHOWWINDOW); + + d->hwnd_notif_aux = hwnd; + +#endif /* _WIN32_IE >= 0x0600 */ + + EndDeferWindowPos(hdefer); + + CopyRect(&d->r_notif, &r_row); + + d->notif_type = NC_NOTIFY_MARQUEE; + + /* Note that we must call nc_layout_main_panel() after calling + this to adjust the layout of the main panel. However we aren't + calling it here since we might want to add another set of + notifications or make other changes to the main panel content + before calling nc_layout_main_panel(). */ +} + +static void +nc_notify_message(khui_nc_wnd_data * d, + kherr_severity severity, + const wchar_t * message) { + + SIZE icon_size; + LPCTSTR icon_res; + HICON h_icon; + HWND hwnd; + HFONT hfont; + HDWP hdefer; + + RECT r_row; + RECT r_label; + RECT r_icon; + + nc_notify_clear(d); + + icon_size.cx = GetSystemMetrics(SM_CXSMICON); + icon_size.cy = GetSystemMetrics(SM_CYSMICON); + + switch(severity) { + case KHERR_INFO: + icon_res = MAKEINTRESOURCE(OIC_INFORMATION); + break; + + case KHERR_WARNING: + icon_res = MAKEINTRESOURCE(OIC_WARNING); + break; + + case KHERR_ERROR: + icon_res = MAKEINTRESOURCE(OIC_ERROR); + break; + + default: + icon_res = NULL; + } + + if (icon_res != NULL) { + h_icon = (HICON) LoadImage(NULL, + icon_res, + IMAGE_ICON, + icon_size.cx, + icon_size.cy, + LR_DEFAULTCOLOR | LR_SHARED); } else { - ShowWindow(hw, SW_SHOW); + h_icon = NULL; + } + + CopyRect(&r_row, &d->r_row); + +#define CENTERVALUE(w,v) ((w)/2 - (v)/2) + + SetRect(&r_icon, + 0, CENTERVALUE(r_row.bottom - r_row.top, icon_size.cy), + icon_size.cx, + CENTERVALUE(r_row.bottom - r_row.top, icon_size.cy) + icon_size.cy); + +#undef CENTERVALUE + + CopyRect(&r_label, &r_row); + OffsetRect(&r_label, -r_label.left, -r_label.top); + r_label.left += (icon_size.cx * 3) / 2; + + { + long y; + + if (IsRectEmpty(&d->r_custprompt)) { + y = d->r_idspec.bottom; + } else { + y = d->r_custprompt.bottom; + } + + OffsetRect(&r_row, d->r_area.left, y); + OffsetRect(&r_label, r_row.left, r_row.top); + OffsetRect(&r_icon, r_row.left, r_row.top); } - SetWindowPos(hw, NULL, - d->r_credtext.left + d->r_n_input.left, /* x */ - d->r_credtext.top, /* y */ - d->r_n_input.right - d->r_n_input.left, /* width */ - d->r_credtext.bottom - d->r_credtext.top, /* height */ - SWP_NOACTIVATE | SWP_NOOWNERZORDER | - SWP_NOZORDER); - - hw = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT_LABEL); - - SetWindowPos(hw, NULL, - d->r_credtext.left + d->r_n_label.left, /* x */ - d->r_credtext.top, /* y */ - d->r_n_label.right - d->r_n_label.left, /* width */ - d->r_n_label.bottom - d->r_n_label.top, /* height */ - SWP_NOACTIVATE | SWP_NOOWNERZORDER | - SWP_NOZORDER); + hfont = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); + + hdefer = BeginDeferWindowPos(2); + + hwnd = CreateWindowEx(0, + L"STATIC", + message, + WS_CHILD | SS_ENDELLIPSIS | SS_CENTER, + r_label.left, r_label.top, + r_label.right - r_label.left, + r_label.bottom - r_label.top, + d->dlg_main, + NULL, NULL, NULL); +#ifdef DEBUG + assert(hwnd != NULL); +#endif + SendMessage(hwnd, WM_SETFONT, (WPARAM) hfont, (LPARAM) TRUE); + + DeferWindowPos(hdefer, hwnd, NULL, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSIZE | SWP_SHOWWINDOW); + + d->hwnd_notif_label = hwnd; + + hwnd = CreateWindowEx(0, + L"STATIC", + NULL, + WS_CHILD | SS_ICON | +#if (_WIN32_IE >= 0x0600) + SS_REALSIZECONTROL +#else + 0 +#endif + , + r_icon.left, r_icon.top, + r_icon.right - r_icon.left, + r_icon.bottom - r_icon.top, + d->dlg_main, + NULL, NULL, NULL); +#ifdef DEBUG + assert(hwnd != NULL); +#endif + + if (h_icon && hwnd) + SendMessage(hwnd, STM_SETICON, (WPARAM) h_icon, 0); + + DeferWindowPos(hdefer, hwnd, NULL, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOZORDER); + + d->hwnd_notif_aux = hwnd; + + EndDeferWindowPos(hdefer); + + CopyRect(&d->r_notif, &r_row); + + d->notif_type = NC_NOTIFY_MESSAGE; + + /* Note that we must call nc_layout_main_panel() after calling + this to adjust the layout of the main panel. However we aren't + calling it here since we might want to add another set of + notifications or make other changes to the main panel content + before calling nc_layout_main_panel(). */ } -/* sorts tab buttons */ -static int __cdecl +static void +nc_layout_main_panel(khui_nc_wnd_data * d) +{ + RECT r_main; + HWND hw_ct; + HWND hw_ct_label; + HDWP hdwp; + RECT r_used; /* extent used by identity specifiers, + custom prompts and notificaiton + controls. */ + + RECT r_wmain; /* extents of the main window in screen + coordinates. */ + + r_main.left = 0; + r_main.top = 0; + r_main.bottom = NCDLG_HEIGHT; + r_main.right = NCDLG_WIDTH; + + MapDialogRect(d->dlg_main, &r_main); + + CopyRect(&r_used, &d->r_idspec); + + GetWindowRect(d->dlg_main, &r_wmain); + + hdwp = BeginDeferWindowPos(7); + + /* check if the notification area and the custom prompt area are + overlapping. */ + + if (d->notif_type != NC_NOTIFY_NONE) { + long delta_y = 0; + RECT r; + + CopyRect(&r, &d->r_custprompt); + + if (IsRectEmpty(&d->r_custprompt)) { + /* if there are no custom prompts, then the notification + area should be immediately below the identitify + specifers. */ + + delta_y = d->r_idspec.bottom - d->r_notif.top; + } else { + /* otherwise, the notification area should be immediately + below the custom prompt area */ + + delta_y = d->r_custprompt.bottom - d->r_notif.top; + } + + if (delta_y != 0) { + RECT r_lbl; + RECT r_aux; + + if (d->hwnd_notif_label) { + GetWindowRect(d->hwnd_notif_label, &r_lbl); + OffsetRect(&r_lbl, -r_wmain.left, delta_y - r_wmain.top); + + DeferWindowPos(hdwp, d->hwnd_notif_label, NULL, + r_lbl.left, r_lbl.top, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_NOSIZE); + } + + if (d->hwnd_notif_aux) { + GetWindowRect(d->hwnd_notif_aux, &r_aux); + OffsetRect(&r_aux, -r_wmain.left, delta_y - r_wmain.top); + + DeferWindowPos(hdwp, d->hwnd_notif_aux, NULL, + r_aux.left, r_aux.top, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_NOSIZE); + } + + OffsetRect(&d->r_notif, 0, delta_y); + } + } + + if (!IsRectEmpty(&d->r_custprompt)) { + r_used.bottom = max(d->r_custprompt.bottom, + r_used.bottom); + } + + if (!IsRectEmpty(&d->r_notif)) { + r_used.bottom = max(d->r_notif.bottom, + r_used.bottom); + } + + if (d->nc->mode == KHUI_NC_MODE_MINI) { + RECT r_ok; + RECT r_cancel; + RECT r_advanced; + HWND hw; + + hw = GetDlgItem(d->dlg_main, IDOK); +#ifdef DEBUG + assert(hw != NULL); +#endif + GetWindowRect(hw, &r_ok); + OffsetRect(&r_ok, -r_wmain.left, -r_ok.top + r_used.bottom); + + DeferWindowPos(hdwp, hw, NULL, + r_ok.left, r_ok.top, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW); + + hw = GetDlgItem(d->dlg_main, IDCANCEL); +#ifdef DEBUG + assert(hw != NULL); +#endif + GetWindowRect(hw, &r_cancel); + OffsetRect(&r_cancel, -r_wmain.left, -r_cancel.top + r_used.bottom); + + DeferWindowPos(hdwp, hw, NULL, + r_cancel.left, r_cancel.top, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW); + + hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED); +#ifdef DEBUG + assert(hw != NULL); +#endif + GetWindowRect(hw, &r_advanced); + OffsetRect(&r_advanced, -r_wmain.left, -r_advanced.top + r_used.bottom); + + DeferWindowPos(hdwp, hw, NULL, + r_advanced.left, r_advanced.top, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW); + + /* and now update the extents of the main panel */ + r_main.bottom = r_used.bottom + (r_ok.bottom - r_ok.top) + d->r_area.top; + + CopyRect(&d->r_main, &r_main); + + } else { + + HWND hw; + + hw = GetDlgItem(d->dlg_main, IDOK); +#ifdef DEBUG + assert(hw != NULL); +#endif + if (IsWindowVisible(hw)) + DeferWindowPos(hdwp, hw, NULL, + 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | + SWP_NOOWNERZORDER | SWP_NOZORDER); + + hw = GetDlgItem(d->dlg_main, IDCANCEL); +#ifdef DEBUG + assert(hw != NULL); +#endif + if (IsWindowVisible(hw)) + DeferWindowPos(hdwp, hw, NULL, + 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | + SWP_NOOWNERZORDER | SWP_NOZORDER); + + hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED); +#ifdef DEBUG + assert(hw != NULL); +#endif + if (IsWindowVisible(hw)) + DeferWindowPos(hdwp, hw, NULL, + 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | + SWP_NOOWNERZORDER | SWP_NOZORDER); + + d->r_credtext.top = r_used.bottom; + + CopyRect(&d->r_main, &r_main); + } + + /* now update the layout of the credentials text window */ + + hw_ct = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT); + hw_ct_label = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT_LABEL); +#ifdef DEBUG + assert(hw_ct != NULL); + assert(hw_ct_label != NULL); +#endif + + if (d->nc->mode == KHUI_NC_MODE_MINI || + d->r_credtext.bottom < d->r_credtext.top + d->r_row.bottom * 2) { + + /* either we aren't supposed to show the credentials text + window, or we don't have enough room. */ + if (IsWindowVisible(hw_ct) || IsWindowVisible(hw_ct_label)) { + + DeferWindowPos(hdwp, hw_ct, NULL, + 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); + + DeferWindowPos(hdwp, hw_ct_label, NULL, + 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); + + } + + } else { + + DeferWindowPos(hdwp, + hw_ct, NULL, + d->r_credtext.left + d->r_n_input.left, /* x */ + d->r_credtext.top, /* y */ + d->r_n_input.right - d->r_n_input.left, /* width */ + d->r_credtext.bottom - d->r_credtext.top, /* height */ + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_SHOWWINDOW); + + DeferWindowPos(hdwp, + hw_ct_label, NULL, + d->r_credtext.left + d->r_n_label.left, /* x */ + d->r_credtext.top, /* y */ + d->r_n_label.right - d->r_n_label.left, /* width */ + d->r_n_label.bottom - d->r_n_label.top, /* height */ + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_SHOWWINDOW); + } + + EndDeferWindowPos(hdwp); + + /* NOTE: although we updated d->r_main, if the new credentials + window is in mini mode, we must call + nc_layout_new_cred_window() to adjust the size of the new + credentials window to fit the main panel. We don't do it here + because we need to keep these two operations separate. */ +} + +/* Credential type panel comparison function. Tabs are sorted based + on the following criteria: + + 1) By ordinal - Panels with ordinal -1 will be ranked after panels + whose ordinal is not -1. + + 2) By name - Case insensitive comparison of the name. If the panel + does not have a name (i.e. the ->name member is NULL, it will be + ranked after panels which have a name. + */ +static int __cdecl nc_tab_sort_func(const void * v1, const void * v2) { /* v1 and v2 and of type : khui_new_creds_by_type ** */ @@ -201,11 +716,19 @@ nc_tab_sort_func(const void * v1, const void * v2) if(t1->ordinal != -1) { if(t2->ordinal != -1) { - if(t1->ordinal == t2->ordinal) - return wcscmp(t1->name, t2->name); - else + if(t1->ordinal == t2->ordinal) { + if (t1->name && t2->name) + return _wcsicmp(t1->name, t2->name); + else if (t1->name) + return -1; + else if (t2->name) + return 1; + else + return 0; + } else { /* safe to convert to an int here */ return (int) (t1->ordinal - t2->ordinal); + } } else return -1; } else { @@ -213,30 +736,30 @@ nc_tab_sort_func(const void * v1, const void * v2) return 1; else if (t1->name && t2->name) return wcscmp(t1->name, t2->name); + else if (t1->name) + return -1; + else if (t2->name) + return 1; else return 0; } } static void -nc_notify_types_async(khui_new_creds * c, UINT uMsg, - WPARAM wParam, LPARAM lParam) +nc_notify_types(khui_new_creds * c, UINT uMsg, + WPARAM wParam, LPARAM lParam, BOOL sync) { khm_size i; for(i=0; in_types; i++) { - PostMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam); - } -} -static void -nc_notify_types(khui_new_creds * c, UINT uMsg, - WPARAM wParam, LPARAM lParam) -{ - khm_size i; + if (c->types[i]->hwnd_panel == NULL) + continue; - for(i=0; in_types; i++) { - SendMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam); + if (sync) + SendMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam); + else + PostMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam); } } @@ -264,6 +787,8 @@ nc_clear_password_fields(khui_nc_wnd_data * d) } } +/* used by nc_enable_controls */ + struct nc_enum_wnd_data { khui_nc_wnd_data * d; khm_boolean enable; @@ -317,9 +842,86 @@ nc_update_credtext(khui_nc_wnd_data * d) StringCchLength(ctbuf, NC_MAXCCH_CREDTEXT, &cch); buf = ctbuf + cch; nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, - MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), (LPARAM) d->nc); + MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), (LPARAM) d->nc, TRUE); /* hopefully all the types have updated their credential texts */ + + /* if the dialog is in the mini mode, we have to display + exceptions using a notification. */ + if (d->nc->mode == KHUI_NC_MODE_MINI) { + BOOL need_layout = FALSE; + if (d->nc->n_identities == 0) { + + /* There are no identities selected. We don't show any + notifications here. */ + if (d->notif_type != NC_NOTIFY_NONE) { + nc_notify_clear(d); + need_layout = TRUE; + } + + } else { + + wchar_t id_name[KCDB_IDENT_MAXCCH_NAME]; + wchar_t format[256]; + wchar_t msg[ARRAYLENGTH(format) + ARRAYLENGTH(id_name)]; + khm_size cbbuf; + khm_int32 flags; + + kcdb_identity_get_flags(d->nc->identities[0], &flags); + + cbbuf = sizeof(id_name); + kcdb_identity_get_name(d->nc->identities[0], id_name, &cbbuf); + + if (flags & KCDB_IDENT_FLAG_INVALID) { + + /* identity is invalid */ + LoadString(khm_hInstance, IDS_NCN_IDENT_INVALID, + format, ARRAYLENGTH(format)); + StringCbPrintf(msg, sizeof(msg), format, id_name); + + nc_notify_message(d, KHERR_ERROR, msg); + + need_layout = TRUE; + + } else if (flags & KCDB_IDENT_FLAG_VALID) { + + /* identity is valid */ + if (d->notif_type != NC_NOTIFY_NONE) { + nc_notify_clear(d); + need_layout = TRUE; + } + + } else if (flags & KCDB_IDENT_FLAG_UNKNOWN) { + + /* unknown state */ + LoadString(khm_hInstance, IDS_NCN_IDENT_UNKNOWN, + format, ARRAYLENGTH(format)); + StringCbPrintf(msg, sizeof(msg), format, id_name); + + nc_notify_message(d, KHERR_WARNING, msg); + + need_layout = TRUE; + + } else { + + /* still checking */ + LoadString(khm_hInstance, IDS_NCN_IDENT_CHECKING, + format, ARRAYLENGTH(format)); + StringCbPrintf(msg, sizeof(msg), format, id_name); + + nc_notify_marquee(d, msg); + + need_layout = TRUE; + + } + } + + if (need_layout) { + nc_layout_main_panel(d); + nc_layout_new_cred_window(d); + } + } + if(d->nc->n_identities == 1) { wchar_t main_fmt[256]; wchar_t id_fmt[256]; @@ -342,6 +944,9 @@ nc_update_credtext(khui_nc_wnd_data * d) } else if(flags & KCDB_IDENT_FLAG_VALID) { LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else if(flags & KCDB_IDENT_FLAG_UNKNOWN) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED, + id_fmt, (int) ARRAYLENGTH(id_fmt)); } else if(d->nc->subtype == KMSG_CRED_NEW_CREDS) { LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_CHECKING, id_fmt, (int) ARRAYLENGTH(id_fmt)); @@ -498,42 +1103,224 @@ nc_update_credtext(khui_nc_wnd_data * d) cbsize = sizeof(wtitle); kcdb_identity_get_name(d->nc->identities[0], wtitle, &cbsize); - if (d->nc->subtype == KMSG_CRED_PASSWORD) - LoadString(khm_hInstance, IDS_WTPOST_PASSWORD, - wpostfix, (int) ARRAYLENGTH(wpostfix)); - else - LoadString(khm_hInstance, IDS_WTPOST_NEW_CREDS, - wpostfix, (int) ARRAYLENGTH(wpostfix)); + if (d->nc->subtype == KMSG_CRED_PASSWORD) + LoadString(khm_hInstance, IDS_WTPOST_PASSWORD, + wpostfix, (int) ARRAYLENGTH(wpostfix)); + else + LoadString(khm_hInstance, IDS_WTPOST_NEW_CREDS, + wpostfix, (int) ARRAYLENGTH(wpostfix)); + + StringCbCat(wtitle, sizeof(wtitle), wpostfix); + + SetWindowText(d->nc->hwnd, wtitle); + } else { + wchar_t wtitle[256]; + + if (d->nc->subtype == KMSG_CRED_PASSWORD) + LoadString(khm_hInstance, IDS_WT_PASSWORD, + wtitle, (int) ARRAYLENGTH(wtitle)); + else + LoadString(khm_hInstance, IDS_WT_NEW_CREDS, + wtitle, (int) ARRAYLENGTH(wtitle)); + + SetWindowText(d->nc->hwnd, wtitle); + } + } + + if (!(d->nc->response & KHUI_NC_RESPONSE_PROCESSING)) { + if(validId || + d->nc->subtype == KMSG_CRED_PASSWORD) { + /* TODO: check if all the required fields have valid values + before enabling the Ok button */ + okEnable = TRUE; + } + + hw = GetDlgItem(d->dlg_main, IDOK); + EnableWindow(hw, okEnable); + hw = GetDlgItem(d->dlg_bb, IDOK); + EnableWindow(hw, okEnable); + } +} + +static void +nc_layout_new_cred_window(khui_nc_wnd_data * ncd) { + khui_new_creds * c; + RECT r_main; + RECT r_ncdialog; + HDWP hdefer; + + c = ncd->nc; + + r_main.left = 0; + r_main.top = 0; + r_main.right = NCDLG_WIDTH; + r_main.bottom = NCDLG_HEIGHT; + + MapDialogRect(ncd->dlg_main, &r_main); + + hdefer = BeginDeferWindowPos(5); + + if (c->mode == KHUI_NC_MODE_MINI) { + + if (IsWindowVisible(ncd->tab_wnd)) { + DeferWindowPos(hdefer, + ncd->tab_wnd, NULL, + 0, 0, 0, 0, + SWP_HIDEWINDOW | + SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSIZE | SWP_NOZORDER); + } + + if (IsWindowVisible(ncd->dlg_bb)) { + DeferWindowPos(hdefer, + ncd->dlg_bb, NULL, + 0, 0, 0, 0, + SWP_HIDEWINDOW | + SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOSIZE | SWP_NOZORDER); + } + + DeferWindowPos(hdefer, ncd->dlg_main, NULL, + r_main.left, r_main.top, + r_main.right - r_main.left, + r_main.bottom - r_main.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_SHOWWINDOW); + + /* note that the ncd->r_main.bottom may not be the same as + r_main.bottom because ncd->r_main.bottom is set dynamically + depending on custom controls. ncd->r_main is valid only + once nc_layout_main_panel() is called.*/ + CopyRect(&ncd->r_required, &ncd->r_main); + + } else { + RECT r_tabctrl; + RECT r_displayarea; + RECT r_bbar; + khm_size i; + + /* calculate the size of the tab control so that it fits + snugly around the expanded main panel. */ + CopyRect(&r_tabctrl, &r_main); + TabCtrl_AdjustRect(ncd->tab_wnd, TRUE, &r_tabctrl); + + if (r_tabctrl.left < 0 || + r_tabctrl.top < 0) { + + OffsetRect(&r_tabctrl, + (r_tabctrl.left < 0)? -r_tabctrl.left : 0, + (r_tabctrl.top < 0)? -r_tabctrl.top : 0); + + } + +#ifdef DEBUG + assert(r_tabctrl.left == 0); + assert(r_tabctrl.top == 0); +#endif + + OffsetRect(&r_tabctrl, 0, ncd->r_area.top); + + /* and now calculate the rectangle where the main panel should + be inside the tab control. */ + CopyRect(&r_displayarea, &r_tabctrl); + TabCtrl_AdjustRect(ncd->tab_wnd, FALSE, &r_displayarea); + + DeferWindowPos(hdefer, + ncd->tab_wnd, HWND_BOTTOM, + r_tabctrl.left, r_tabctrl.top, + r_tabctrl.right - r_tabctrl.left, + r_tabctrl.bottom - r_tabctrl.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_SHOWWINDOW); + + /* we have to place the button bar just to the right of the + tab panel. */ + r_bbar.left = 0; + r_bbar.top = 0; + r_bbar.right = NCDLG_BBAR_WIDTH; + r_bbar.bottom = NCDLG_BBAR_HEIGHT; + + MapDialogRect(ncd->dlg_main, &r_bbar); + + OffsetRect(&r_bbar, r_tabctrl.right, 0); + + DeferWindowPos(hdefer, + ncd->dlg_bb, NULL, + r_bbar.left, r_bbar.top, + r_bbar.right - r_bbar.left, + r_bbar.bottom - r_bbar.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | SWP_SHOWWINDOW); + + /* move the main panel inside the tab control... */ + DeferWindowPos(hdefer, + ncd->dlg_main, NULL, + r_displayarea.left, r_displayarea.top, + r_displayarea.right - r_displayarea.left, + r_displayarea.bottom - r_displayarea.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | + (ncd->current_panel == 0 ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); + + /* and also move all the credential type panels (if they have + been created) inside the tab control too. */ + khui_cw_lock_nc(c); + + for (i=0; i < c->n_types; i++) { + if (c->types[i]->hwnd_panel != NULL) { + DeferWindowPos(hdefer, + c->types[i]->hwnd_panel, NULL, + r_displayarea.left, r_displayarea.top, + r_displayarea.right - r_displayarea.left, + r_displayarea.bottom - r_displayarea.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER | + (ncd->current_panel == c->types[i]->ordinal ? + SWP_SHOWWINDOW : SWP_HIDEWINDOW)); + } + } + + khui_cw_unlock_nc(c); + + /* then update the required size of the new credentials + dialog. */ + ncd->r_required.left = 0; + ncd->r_required.top = 0; + ncd->r_required.right = r_bbar.right; + ncd->r_required.bottom = max(r_tabctrl.bottom, r_bbar.bottom) + ncd->r_area.top; + } + + /* commit all the window moves, resizes and hides/shows we did*/ + EndDeferWindowPos(hdefer); + + /* now we have to see if the client area of the new credentials + window is the right size. */ + + GetClientRect(c->hwnd, &r_ncdialog); - StringCbCat(wtitle, sizeof(wtitle), wpostfix); + if ( - SetWindowText(d->nc->hwnd, wtitle); - } else { - wchar_t wtitle[256]; + ((r_ncdialog.right - r_ncdialog.left != + ncd->r_required.right - ncd->r_required.left) - if (d->nc->subtype == KMSG_CRED_PASSWORD) - LoadString(khm_hInstance, IDS_WT_PASSWORD, - wtitle, (int) ARRAYLENGTH(wtitle)); - else - LoadString(khm_hInstance, IDS_WT_NEW_CREDS, - wtitle, (int) ARRAYLENGTH(wtitle)); + || - SetWindowText(d->nc->hwnd, wtitle); - } - } + (r_ncdialog.bottom - r_ncdialog.top != + ncd->r_required.bottom - ncd->r_required.top)) - if (!(d->nc->response & KHUI_NC_RESPONSE_PROCESSING)) { - if(validId || - d->nc->subtype == KMSG_CRED_PASSWORD) { - /* TODO: check if all the required fields have valid values - before enabling the Ok button */ - okEnable = TRUE; - } + && - hw = GetDlgItem(d->dlg_main, IDOK); - EnableWindow(hw, okEnable); - hw = GetDlgItem(d->dlg_bb, IDOK); - EnableWindow(hw, okEnable); + /* we don't bother if the new creds window is already in the + process of changing the size. */ + !ncd->size_changing) { + + /* if not, notify the window that the size needs adjusting. */ + if (IsWindowVisible(c->hwnd)) + PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_LAYOUT), 0); + else + SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_LAYOUT), 0); } } @@ -557,7 +1344,7 @@ nc_handle_wm_create(HWND hwnd, int x, y; int width, height; RECT r; - khm_int32 t; + HFONT hf_main; lpc = (LPCREATESTRUCT) lParam; @@ -568,34 +1355,70 @@ nc_handle_wm_create(HWND hwnd, ncd->nc = c; c->hwnd = hwnd; +#ifdef DEBUG + assert(c->subtype == KMSG_CRED_NEW_CREDS || + c->subtype == KMSG_CRED_PASSWORD); +#endif + #pragma warning(push) #pragma warning(disable: 4244) SetWindowLongPtr(hwnd, CW_PARAM, (LONG_PTR) ncd); #pragma warning(pop) - /* first try to create the main dialog panel */ - - assert(c->subtype == KMSG_CRED_NEW_CREDS || - c->subtype == KMSG_CRED_PASSWORD); + /* first, create the tab control that will house the main dialog + panel as well as the plug-in specific panels */ + ncd->tab_wnd = CreateWindowEx(0, /* extended style */ + WC_TABCONTROL, + L"TabControloxxrz", /* window name */ + TCS_HOTTRACK | TCS_RAGGEDRIGHT | + TCS_SINGLELINE | TCS_TABS | + WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS, + 0, 0, 100, 100, /* x,y,width height. + We'll be changing + these later + anyway. */ + hwnd, + (HMENU) IDC_NC_TABS, + NULL, + 0); + +#ifdef DEBUG + assert(ncd->tab_wnd != NULL); +#endif + + /* try to create the main dialog panel */ ncd->dlg_main = CreateDialogParam(khm_hInstance, - MAKEINTRESOURCE(IDD_NC_PASSWORD), + MAKEINTRESOURCE(IDD_NC_NEWCRED), hwnd, nc_common_dlg_proc, (LPARAM) ncd); #ifdef DEBUG - assert(ncd->dlg_main); + assert(ncd->dlg_main != NULL); #endif + hf_main = (HFONT) SendMessage(ncd->dlg_main, WM_GETFONT, 0, 0); + if (hf_main) + SendMessage(ncd->tab_wnd, WM_SETFONT, (WPARAM) hf_main, FALSE); + { RECT r_main; RECT r_area; RECT r_row; HWND hw; - /* pick out metrics for use by the custom prompter stuff */ + /* During the operation of the new credentials window, we will + need to dynamically change the layout of the controls as a + result of custom prompting from credentials providers and + identity selectors from identity providers. In order to + guide the dynamic layout, we pick out a few metrics from + the dialog template for the main panel. The metrics come + from hidden STATIC controls in the dialog template. */ + GetWindowRect(ncd->dlg_main, &r_main); + /* IDC_NC_TPL_PANEL spans the full extent of the dialog that + we can populate with custom controls. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_PANEL); #ifdef DEBUG assert(hw); @@ -604,6 +1427,9 @@ nc_handle_wm_create(HWND hwnd, OffsetRect(&r_area,-r_main.left, -r_main.top); CopyRect(&ncd->r_area, &r_area); + /* IDC_NC_TPL_ROW spans the extent of a row of normal sized + custom controls. A row of custom controls typicall consist + of a text label and an input control. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW); #ifdef DEBUG assert(hw); @@ -613,6 +1439,9 @@ nc_handle_wm_create(HWND hwnd, OffsetRect(&r,-r.left, -r.top); CopyRect(&ncd->r_row, &r); + /* IDC_NC_TPL_LABEL spans the extent that a normal sized + label. The control overlaps IDC_NC_TPL_ROW so we can get + coordinates relative to the row extents. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL); #ifdef DEBUG assert(hw); @@ -621,6 +1450,9 @@ nc_handle_wm_create(HWND hwnd, OffsetRect(&r,-r_row.left, -r_row.top); CopyRect(&ncd->r_n_label, &r); + /* IDC_NC_TPL_INPUT spans the extent of a normal sized input + control in a custom control row. The control overlaps + IDC_NC_TPL_ROW so we can get relative coordinates. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT); #ifdef DEBUG assert(hw); @@ -629,12 +1461,16 @@ nc_handle_wm_create(HWND hwnd, OffsetRect(&r, -r_row.left, -r_row.top); CopyRect(&ncd->r_n_input, &r); + /* IDC_NC_TPL_ROW_LG spans the extent of a row of large sized + controls. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW_LG); #ifdef DEBUG assert(hw); #endif GetWindowRect(hw, &r_row); + /* IDC_NC_TPL_LABEL_LG is a large sized label. The control + overlaps IDC_NC_TPL_ROW_LG. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL_LG); #ifdef DEBUG assert(hw); @@ -643,6 +1479,8 @@ nc_handle_wm_create(HWND hwnd, OffsetRect(&r, -r_row.left, -r_row.top); CopyRect(&ncd->r_e_label, &r); + /* IDC_NC_TPL_INPUT_LG is a large sized input control. + Overlaps IDC_NC_TPL_ROW_LG. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT_LG); #ifdef DEBUG assert(hw); @@ -656,6 +1494,11 @@ nc_handle_wm_create(HWND hwnd, ncd->r_idspec.bottom = ncd->r_idspec.top; + /* And finally the credential text window. The only metric we + take from here is the Y coordinate of the bottom of the + control since the actual size and position of the + credentials window will change depending on the custom + controls being displayed. */ hw = GetDlgItem(ncd->dlg_main, IDC_NC_CREDTEXT); #ifdef DEBUG assert(hw); @@ -668,35 +1511,17 @@ nc_handle_wm_create(HWND hwnd, /* if the mode is 'mini'*/ r.left = 0; r.top = 0; + if(c->mode == KHUI_NC_MODE_MINI) { r.right = NCDLG_WIDTH; r.bottom = NCDLG_HEIGHT; } else { r.right = NCDLG_WIDTH + NCDLG_BBAR_WIDTH; - r.bottom = NCDLG_HEIGHT + NCDLG_TAB_HEIGHT; + r.bottom = NCDLG_BBAR_HEIGHT; } MapDialogRect(ncd->dlg_main, &r); - ncd->r_main.left = 0; - ncd->r_main.top = 0; - ncd->r_main.right = NCDLG_WIDTH; - ncd->r_main.bottom = NCDLG_HEIGHT; - - ncd->r_ts.left = 0; - ncd->r_ts.top = ncd->r_main.bottom; - ncd->r_ts.right = ncd->r_main.right; - ncd->r_ts.bottom = ncd->r_ts.top + NCDLG_TAB_HEIGHT; - - ncd->r_bb.left = ncd->r_main.right; - ncd->r_bb.top = 0; - ncd->r_bb.right = ncd->r_bb.left + NCDLG_BBAR_WIDTH; - ncd->r_bb.bottom = ncd->r_ts.bottom; - - MapDialogRect(ncd->dlg_main, &(ncd->r_main)); - MapDialogRect(ncd->dlg_main, &(ncd->r_ts)); - MapDialogRect(ncd->dlg_main, &(ncd->r_bb)); - /* center the new creds window over the main NetIDMgr window */ width = r.right - r.left; height = r.bottom - r.top; @@ -721,18 +1546,6 @@ nc_handle_wm_create(HWND hwnd, MoveWindow(hwnd, x, y, width, height, FALSE); - SetWindowPos(ncd->dlg_main, - NULL, - ncd->r_main.left, - ncd->r_main.top, - ncd->r_main.right - ncd->r_main.left, - ncd->r_main.bottom - ncd->r_main.top, - SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | - SWP_NOREDRAW | SWP_NOZORDER); - - /* IDD_NC_BBAR is the button bar that sits on the right of the - dialog when the new creds window is in 'expanded' mode. */ - ncd->dlg_bb = CreateDialogParam(khm_hInstance, MAKEINTRESOURCE(IDD_NC_BBAR), hwnd, @@ -743,101 +1556,22 @@ nc_handle_wm_create(HWND hwnd, assert(ncd->dlg_bb); #endif - SetWindowPos(ncd->dlg_bb, - NULL, - ncd->r_bb.left, - ncd->r_bb.top, - ncd->r_bb.right - ncd->r_bb.left, - ncd->r_bb.bottom - ncd->r_bb.top, - SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | - SWP_NOREDRAW | SWP_NOZORDER); - - /* IDD_NC_TS is the tab strip that sits below the main panel when - the new creds window is in 'expanded' mode */ - - ncd->dlg_ts = CreateDialogParam(khm_hInstance, - MAKEINTRESOURCE(IDD_NC_TS), - hwnd, - nc_common_dlg_proc, - (LPARAM) ncd); - -#ifdef DEBUG - assert(ncd->dlg_ts); -#endif - - SetWindowPos(ncd->dlg_ts, - NULL, - ncd->r_ts.left, - ncd->r_ts.top, - ncd->r_ts.right - ncd->r_ts.left, - ncd->r_ts.bottom - ncd->r_ts.top, - SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | - SWP_NOREDRAW | SWP_NOZORDER); - - if(c->mode == KHUI_NC_MODE_MINI) { - /* hide and show stuff */ - ShowWindow(ncd->dlg_main, SW_SHOW); - ShowWindow(ncd->dlg_bb, SW_HIDE); - ShowWindow(ncd->dlg_ts, SW_HIDE); - - nc_position_credtext(ncd); - - } else { - /* hide and show stuff */ - ShowWindow(ncd->dlg_main, SW_SHOW); - ShowWindow(ncd->dlg_bb, SW_SHOW); - ShowWindow(ncd->dlg_ts, SW_SHOW); - - PostMessage(ncd->dlg_main, KHUI_WM_NC_NOTIFY, - MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0); - } - /* Call the identity provider callback to set the identity - selector controls */ + selector controls. These controls need to be there before we + layout the main panel. */ c->ident_cb(c, WMNC_IDENT_INIT, NULL, 0, 0, (LPARAM) ncd->dlg_main); -#if 0 - { - HWND hw; - wchar_t wcaption[64]; - - LoadString(khm_hInstance, IDS_NC_SETDEF, wcaption, - ARRAYLENGTH(wcaption)); - - /* Now create the set as default button */ - hw = CreateWindow - (L"BUTTON", - wcaption, - WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX, - 0, 0, 100, 100, - ncd->dlg_main, - (HMENU) NC_BN_SET_DEF_ID, - khm_hInstance, - NULL); - - nc_add_control_row(ncd, NULL, hw, KHUI_CTRLSIZE_HALF); + if (c->mode == KHUI_NC_MODE_EXPANDED) { + SendMessage(ncd->dlg_main, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0); + } else { + /* we don't call this if the dialog is expanded because + posting WMNC_DIALOG_EXPAND to the main panel results in + this getting called anyway. */ + nc_layout_main_panel(ncd); } -#endif - /* we defer the creation of the tab buttons for later */ - /* bring the window to the top, if necessary */ - if (KHM_SUCCEEDED(khc_read_int32(NULL, - L"CredWindow\\Windows\\NewCred\\ForceToTop", - &t)) && - t != 0 && - !khm_is_dialog_active()) { - - /* if the main window is not visible, then the SetWindowPos() - call is sufficient to bring the new creds window to the - top. However, if the main window is visible but not - active, the main window needs to be activated before a - child window can be activated. */ - khm_activate_main_window(); - - SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, - (SWP_NOMOVE | SWP_NOSIZE)); - - } + nc_layout_new_cred_window(ncd); /* add this to the dialog chain */ khm_add_dialog(hwnd); @@ -845,6 +1579,7 @@ nc_handle_wm_create(HWND hwnd, return TRUE; } +/* add a control row supplied by an identity provider */ static void nc_add_control_row(khui_nc_wnd_data * d, HWND label, @@ -855,6 +1590,7 @@ nc_add_control_row(khui_nc_wnd_data * d, RECT r_label; RECT r_input; HFONT hf; + HDWP hdefer; hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); SendMessage(label, WM_SETFONT, (WPARAM) hf, FALSE); @@ -890,35 +1626,39 @@ nc_add_control_row(khui_nc_wnd_data * d, return; } + hdefer = BeginDeferWindowPos(2); + if (label) - SetWindowPos(label, - ((d->hwnd_last_idspec != NULL)? - d->hwnd_last_idspec: - HWND_TOP), - r_label.left, r_label.top, - r_label.right - r_label.left, - r_label.bottom - r_label.top, - SWP_DEFERERASE | SWP_NOACTIVATE | - SWP_NOOWNERZORDER); + DeferWindowPos(hdefer, label, + ((d->hwnd_last_idspec != NULL)? + d->hwnd_last_idspec: + HWND_TOP), + r_label.left, r_label.top, + r_label.right - r_label.left, + r_label.bottom - r_label.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER); if (input) - SetWindowPos(input, - (label ? label : ((d->hwnd_last_idspec != NULL)? - d->hwnd_last_idspec: - HWND_TOP)), - r_input.left, r_input.top, - r_input.right - r_input.left, - r_input.bottom - r_input.top, - SWP_DEFERERASE | SWP_NOACTIVATE | - SWP_NOOWNERZORDER); - - d->hwnd_last_idspec = input; + DeferWindowPos(hdefer, input, + (label ? label : ((d->hwnd_last_idspec != NULL)? + d->hwnd_last_idspec: + HWND_TOP)), + r_input.left, r_input.top, + r_input.right - r_input.left, + r_input.bottom - r_input.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER); + + EndDeferWindowPos(hdefer); + + d->hwnd_last_idspec = (input ? input : label); d->r_idspec.bottom = r_row.bottom; - d->r_credtext.top = r_row.bottom; + /* we don't update the layout of the main panel yet, since these + control additions happen before the main panel is displayed. A + call to nc_layout_main_panel() will be made before the main + panel is shown anyway. */ - nc_position_credtext(d); } @@ -929,7 +1669,6 @@ nc_handle_wm_destroy(HWND hwnd, LPARAM lParam) { khui_nc_wnd_data * d; - khm_size i; /* remove self from dialog chain */ khm_del_dialog(hwnd); @@ -938,25 +1677,18 @@ nc_handle_wm_destroy(HWND hwnd, d->nc->ident_cb(d->nc, WMNC_IDENT_EXIT, NULL, 0, 0, 0); - if(d->hwnd_tc_main) - DestroyWindow(d->hwnd_tc_main); - for(i=0;inc->n_types;i++) { - if(d->nc->types[i]->hwnd_tc) { - DestroyWindow(d->nc->types[i]->hwnd_tc); - d->nc->types[i]->hwnd_tc = NULL; - } - } + if (d->hwnd_notif_label) + DestroyWindow(d->hwnd_notif_label); + if (d->hwnd_notif_aux) + DestroyWindow(d->hwnd_notif_aux); if(d->dlg_bb) DestroyWindow(d->dlg_bb); if(d->dlg_main) DestroyWindow(d->dlg_main); - if(d->dlg_ts) - DestroyWindow(d->dlg_ts); d->dlg_bb = NULL; d->dlg_main = NULL; - d->dlg_ts = NULL; PFREE(d); @@ -970,7 +1702,6 @@ nc_handle_wm_command(HWND hwnd, LPARAM lParam) { khui_nc_wnd_data * d; - int id; d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); @@ -993,7 +1724,8 @@ nc_handle_wm_command(HWND hwnd, nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, MAKEWPARAM(0,WMNC_DIALOG_PREPROCESS), - (LPARAM) d->nc); + (LPARAM) d->nc, + TRUE); khui_cw_sync_prompt_values(d->nc); @@ -1009,7 +1741,7 @@ nc_handle_wm_command(HWND hwnd, EnableWindow(hw, FALSE); hw = GetDlgItem(d->dlg_main, IDCANCEL); EnableWindow(hw, FALSE); - hw = GetDlgItem(d->dlg_main, IDC_NC_OPTIONS); + hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED); EnableWindow(hw, FALSE); hw = GetDlgItem(d->dlg_bb, IDOK); EnableWindow(hw, FALSE); @@ -1022,7 +1754,7 @@ nc_handle_wm_command(HWND hwnd, khm_html_help(hwnd, NULL, HH_HELP_CONTEXT, IDH_ACTION_NEW_ID); return FALSE; - case IDC_NC_OPTIONS: + case IDC_NC_ADVANCED: /* the Options button in the main window was clicked. we respond by expanding the dialog. */ PostMessage(hwnd, KHUI_WM_NC_NOTIFY, @@ -1075,7 +1807,7 @@ nc_handle_wm_command(HWND hwnd, KHM_SUCCEEDED(khui_cw_find_type(d->nc, credtype, &t))){ *colon = L':'; - if (t->ordinal != d->ctab && + if (t->ordinal != d->current_panel && *(colon + 1) != L'!') PostMessage(hwnd, KHUI_WM_NC_NOTIFY, @@ -1101,7 +1833,7 @@ nc_handle_wm_command(HWND hwnd, &credtype)) && KHM_SUCCEEDED(khui_cw_find_type(d->nc, credtype, &t))) { - if (t->ordinal != d->ctab) + if (t->ordinal != d->current_panel) PostMessage(hwnd, KHUI_WM_NC_NOTIFY, MAKEWPARAM(t->ordinal, @@ -1127,17 +1859,6 @@ nc_handle_wm_command(HWND hwnd, } return FALSE; #endif - - default: - /* if one of the tab strip buttons were pressed, then - we should switch to that panel */ - id = LOWORD(wParam); - if(id >= NC_TS_CTRL_ID_MIN && id <= NC_TS_CTRL_ID_MAX) { - id -= NC_TS_CTRL_ID_MIN; - PostMessage(hwnd, KHUI_WM_NC_NOTIFY, - MAKEWPARAM(id, WMNC_DIALOG_SWITCH_PANEL),0); - return FALSE; - } } break; } @@ -1155,7 +1876,7 @@ static LRESULT nc_handle_wm_moving(HWND hwnd, d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, - MAKEWPARAM(0, WMNC_DIALOG_MOVE), (LPARAM) d->nc); + MAKEWPARAM(0, WMNC_DIALOG_MOVE), (LPARAM) d->nc, TRUE); return FALSE; } @@ -1166,9 +1887,7 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, LPARAM lParam) { khui_nc_wnd_data * d; - RECT r; - int width, height; - khm_size id; + int id; d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); @@ -1176,37 +1895,21 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, case WMNC_DIALOG_SWITCH_PANEL: id = LOWORD(wParam); - if(id >= 0 && id <= d->nc->n_types) { + if(id >= 0 && id <= (int) d->nc->n_types) { /* one of the tab buttons were pressed */ - if(d->ctab == id) { - return TRUE; /* nothign to do */ + if(d->current_panel == id) { + return TRUE; /* nothing to do */ } - if(d->ctab == 0) { - ShowWindow(d->dlg_main, SW_HIDE); - SendMessage(d->hwnd_tc_main, - BM_SETCHECK, BST_UNCHECKED, 0); - } else { - ShowWindow(d->nc->types[d->ctab - 1]->hwnd_panel, SW_HIDE); - SendMessage(d->nc->types[d->ctab - 1]->hwnd_tc, - BM_SETCHECK, BST_UNCHECKED, 0); - } + d->current_panel = id; - d->ctab = id; - - if(d->ctab == 0) { - ShowWindow(d->dlg_main, SW_SHOW); - SendMessage(d->hwnd_tc_main, - BM_SETCHECK, BST_CHECKED, 0); - } else { - ShowWindow(d->nc->types[id - 1]->hwnd_panel, SW_SHOW); - SendMessage(d->nc->types[id - 1]->hwnd_tc, - BM_SETCHECK, BST_CHECKED, 0); - } + TabCtrl_SetCurSel(d->tab_wnd, id); } - if(d->nc->mode == KHUI_NC_MODE_EXPANDED) + if(d->nc->mode == KHUI_NC_MODE_EXPANDED) { + nc_layout_new_cred_window(d); return TRUE; + } /*else*/ /* fallthrough */ @@ -1219,47 +1922,16 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, d->nc->mode = KHUI_NC_MODE_EXPANDED; - r.top = 0; - r.left = 0; - r.right = NCDLG_WIDTH + NCDLG_BBAR_WIDTH; - r.bottom = NCDLG_HEIGHT + NCDLG_TAB_HEIGHT; - - MapDialogRect(d->dlg_main, &r); - - width = r.right - r.left; - height = r.bottom - r.top; - - /* adjust width and height to accomodate NC area */ - { - RECT wr,cr; - - GetWindowRect(hwnd, &wr); - GetClientRect(hwnd, &cr); + nc_notify_clear(d); - /* the non-client and client areas have already been - calculated at this point. We just use the difference - to adjust the width and height */ - width += (wr.right - wr.left) - (cr.right - cr.left); - height += (wr.bottom - wr.top) - (cr.bottom - cr.top); - } - - SendMessage(d->dlg_main, - KHUI_WM_NC_NOTIFY, - MAKEWPARAM(0,WMNC_DIALOG_EXPAND), - 0); + nc_layout_main_panel(d); - SetWindowPos(hwnd, - NULL, - 0, 0, - width, height, - SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | - SWP_NOZORDER); + nc_layout_new_cred_window(d); - ShowWindow(d->dlg_bb, SW_SHOW); - ShowWindow(d->dlg_ts, SW_SHOW); break; case WMNC_DIALOG_SETUP: + if(d->nc->n_types > 0) { khm_size i; for(i=0; i < d->nc->n_types;i++) { @@ -1281,150 +1953,79 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, } } } + break; case WMNC_DIALOG_ACTIVATE: { - int x,y,width,height; - RECT r; - int id; - wchar_t wbuf[256]; - HFONT hf; - - /* now we create all the tab strip controls */ - r.left = 0; - r.top = 0; - r.right = NCDLG_TAB_WIDTH; - r.bottom = NCDLG_TAB_HEIGHT; - MapDialogRect(d->dlg_main, &r); - - width = r.right - r.left; - height = r.bottom - r.top; - - x = 0; - y = 0; + wchar_t wname[KCDB_MAXCCH_NAME]; + TCITEM tabitem; + khm_int32 t; - id = NC_TS_CTRL_ID_MIN; + /* About to activate the window. We should add all the + panels to the tab control. */ - /* if we have too many buttons than would fit on the - button bar, we have to adjust the width of the buttons. - Of course, having too many of them would be bad and - make the buttons fairly useless. This is just an - interim measure. */ +#ifdef DEBUG + assert(d->tab_wnd != NULL); +#endif - khui_cw_lock_nc(d->nc); + ZeroMemory(&tabitem, sizeof(tabitem)); - GetWindowRect(d->dlg_ts, &r); - if (x + width * (d->nc->n_types + 1) > (khm_size) (r.right - r.left)) { - width = (int)(((r.right - r.left) - x) / (d->nc->n_types + 1)); - } + tabitem.mask = TCIF_PARAM | TCIF_TEXT; - /* first, the control for the main panel */ LoadString(khm_hInstance, IDS_NC_IDENTITY, - wbuf, ARRAYLENGTH(wbuf)); - - d->hwnd_tc_main = - CreateWindow(L"BUTTON", - wbuf, - WS_VISIBLE | WS_CHILD | WS_TABSTOP | - BS_PUSHLIKE | BS_CHECKBOX | BS_TEXT, - x,y,width,height, - d->dlg_ts, - (HMENU)(INT_PTR) id, - khm_hInstance, - NULL); + wname, ARRAYLENGTH(wname)); - hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); - SendMessage(d->hwnd_tc_main, WM_SETFONT, (WPARAM) hf, 0); - SendMessage(d->hwnd_tc_main, BM_SETCHECK, BST_CHECKED, 0); + tabitem.pszText = wname; + tabitem.lParam = 0; /* ordinal */ + + TabCtrl_InsertItem(d->tab_wnd, 0, &tabitem); - id++; - x += width; + khui_cw_lock_nc(d->nc); if(d->nc->n_types > 0) { khm_size i; - /* we should sort the tabs first */ + + /* We should sort the tabs first. See + nc_tab_sort_func() for sort criteria. */ qsort(d->nc->types, d->nc->n_types, sizeof(*(d->nc->types)), nc_tab_sort_func); for(i=0; i < d->nc->n_types;i++) { - wchar_t * name = NULL; d->nc->types[i]->ordinal = i + 1; if(d->nc->types[i]->name) - name = d->nc->types[i]->name; + tabitem.pszText = d->nc->types[i]->name; else { khm_size cbsize; - if(kcdb_credtype_describe - (d->nc->types[i]->type, - NULL, - &cbsize, - KCDB_TS_SHORT) == KHM_ERROR_TOO_LONG) { - - name = PMALLOC(cbsize); - kcdb_credtype_describe(d->nc->types[i]->type, - name, - &cbsize, - KCDB_TS_SHORT); - } else { + cbsize = sizeof(wname); + + if(KHM_FAILED + (kcdb_credtype_describe + (d->nc->types[i]->type, + wname, + &cbsize, + KCDB_TS_SHORT))) { + #ifdef DEBUG assert(FALSE); #endif - continue; - } - } + wname[0] = L'\0'; - d->nc->types[i]->hwnd_tc = - CreateWindow(L"BUTTON", - name, - WS_VISIBLE | WS_CHILD | WS_TABSTOP | - BS_PUSHLIKE | BS_CHECKBOX | BS_TEXT | - ((d->nc->types[i]->hwnd_panel == NULL)? - WS_DISABLED : 0), - x,y,width,height, - d->dlg_ts, - (HMENU)(INT_PTR) id, - khm_hInstance, - NULL); - - SendMessage(d->nc->types[i]->hwnd_tc, WM_SETFONT, - (WPARAM)hf, 0); - -#if 0 - if(d->nc->types[i]->flags & KHUI_NCT_FLAG_DISABLED) - SendMessage(d->nc->types[i]->hwnd_tc, - BM_SETIMAGE, - IMAGE_ICON, - LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_DISABLED))); - else - SendMessage(d->nc->types[i]->hwnd_tc, - BM_SETIMAGE, - IMAGE_ICON, - LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_ENABLED))); -#endif + } - id++; - x += width; + tabitem.pszText = wname; - if(!(d->nc->types[i]->name)) - PFREE(name); + } - /* Now set the position of the type panel */ - ShowWindow(d->nc->types[i]->hwnd_panel, SW_HIDE); - SetWindowPos(d->nc->types[i]->hwnd_panel, - NULL, - d->r_main.left, - d->r_main.top, - d->r_main.right - d->r_main.left, - d->r_main.bottom - d->r_main.top, - SWP_DEFERERASE | SWP_NOACTIVATE | - SWP_NOOWNERZORDER | SWP_NOREDRAW | - SWP_NOZORDER); + tabitem.lParam = d->nc->types[i]->ordinal; + TabCtrl_InsertItem(d->tab_wnd, d->nc->types[i]->ordinal, + &tabitem); } } @@ -1432,7 +2033,39 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, nc_update_credtext(d); + TabCtrl_SetCurSel(d->tab_wnd, 0); /* the first selected + tab is the main + panel. */ + + /* bring the window to the top, if necessary */ + if (KHM_SUCCEEDED(khc_read_int32(NULL, + L"CredWindow\\Windows\\NewCred\\ForceToTop", + &t)) && + + t != 0 && + + !khm_is_dialog_active()) { + + /* if the main window is not visible, then the SetWindowPos() + call is sufficient to bring the new creds window to the + top. However, if the main window is visible but not + active, the main window needs to be activated before a + child window can be activated. */ + khm_activate_main_window(); + + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, + (SWP_NOMOVE | SWP_NOSIZE)); + } + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + + /* we don't enable animations until a specific timeout + elapses after showing the window. We don't need to + animate any size changes if the user has barely had a + chance to notice the original size. This prevents the + new cred window from appearing in an animated state. */ + SetTimer(hwnd, NC_TIMER_ENABLEANIMATE, ENABLEANIMATE_TIMEOUT, NULL); + SetFocus(hwnd); if (d->nc->n_identities == 0) @@ -1446,7 +2079,8 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, BOOL okEnable = FALSE; nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, - MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), (LPARAM) d->nc); + MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), (LPARAM) d->nc, + TRUE); if (d->nc->subtype == KMSG_CRED_NEW_CREDS && d->nc->n_identities > 0 && @@ -1503,9 +2137,11 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, khui_cw_unlock_nc(d->nc); - d->r_credtext.top = d->r_idspec.bottom; + SetRectEmpty(&d->r_custprompt); - nc_position_credtext(d); + nc_layout_main_panel(d); + + nc_layout_new_cred_window(d); } break; @@ -1521,6 +2157,10 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, /* we assume that WMNC_CLEAR_PROMPTS has already been received */ +#ifdef DEBUG + assert(IsRectEmpty(&d->r_custprompt)); +#endif + khui_cw_lock_nc(d->nc); #if 0 @@ -1542,17 +2182,12 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, #endif /* for everything else */ - /* hide the stock password controls */ -#if 0 - /* TAGREMOVE */ - hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD); - ShowWindow(hw, SW_HIDE); - hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD_LABEL); - ShowWindow(hw, SW_HIDE); -#endif - y = d->r_idspec.bottom; + d->r_custprompt.left = d->r_area.left; + d->r_custprompt.right = d->r_area.right; + d->r_custprompt.top = y; + hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); if (d->nc->pname != NULL) { @@ -1752,9 +2387,14 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, khui_cw_unlock_nc(d->nc); - d->r_credtext.top = y; + d->r_custprompt.bottom = y; + + if (d->r_custprompt.bottom == d->r_custprompt.top) + SetRectEmpty(&d->r_custprompt); + + nc_layout_main_panel(d); - nc_position_credtext(d); + nc_layout_new_cred_window(d); } break; @@ -1778,7 +2418,7 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, EnableWindow(hw, TRUE); hw = GetDlgItem(d->dlg_main, IDCANCEL); EnableWindow(hw, TRUE); - hw = GetDlgItem(d->dlg_main, IDC_NC_OPTIONS); + hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED); EnableWindow(hw, TRUE); hw = GetDlgItem(d->dlg_bb, IDOK); EnableWindow(hw, TRUE); @@ -1811,11 +2451,260 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, nc_add_control_row(d, row->label, row->input, row->size); } break; + + case WMNC_UPDATE_LAYOUT: + { + + RECT r_client; + khm_int32 animate; + khm_int32 steps; + khm_int32 timeout; + + /* We are already adjusting the size of the window. The + next time the timer fires, it will notice if the target + size has changed. */ + if (d->size_changing) + return TRUE; + + GetClientRect(hwnd, &r_client); + + if ((r_client.right - r_client.left == + d->r_required.right - d->r_required.left) && + (r_client.bottom - r_client.top == + d->r_required.bottom - d->r_required.top)) { + + /* the window is already at the right size */ + return TRUE; + + } + + if (!IsWindowVisible(hwnd)) { + /* The window is not visible yet. There's no need to + animate anything. */ + + animate = FALSE; + + } else if (KHM_FAILED(khc_read_int32(NULL, + L"CredWindow\\Windows\\NewCred\\AnimateSizeChanges", + &animate))) { +#ifdef DEBUG + assert(FALSE); +#endif + animate = TRUE; + } + + /* if we aren't animating the window resize, then we just + do it in one call. */ + if (!animate || !d->animation_enabled) { + RECT r_window; + + CopyRect(&r_window, &d->r_required); + AdjustWindowRectEx(&r_window, NC_WINDOW_STYLES, FALSE, + NC_WINDOW_EX_STYLES); + + SetWindowPos(hwnd, NULL, 0, 0, + r_window.right - r_window.left, + r_window.bottom - r_window.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOZORDER); + + return TRUE; + } + + if (KHM_FAILED(khc_read_int32(NULL, + L"CredWindow\\Windows\\NewCred\\AnimationSteps", + &steps))) { +#ifdef DEBUG + assert(FALSE); +#endif + steps = NC_SZ_STEPS_DEF; + } else { + + if (steps < NC_SZ_STEPS_MIN) + steps = NC_SZ_STEPS_MIN; + else if (steps > NC_SZ_STEPS_MAX) + steps = NC_SZ_STEPS_MAX; + + } + + if (KHM_FAILED(khc_read_int32(NULL, + L"CredWindow\\Windows\\NewCred\\AnimationStepTimeout", + &timeout))) { +#ifdef DEBUG + assert(FALSE); +#endif + timeout = NC_SZ_TIMEOUT_DEF; + } else { + + if (timeout < NC_SZ_TIMEOUT_MIN) + timeout = NC_SZ_TIMEOUT_MIN; + else if (timeout > NC_SZ_TIMEOUT_MAX) + timeout = NC_SZ_TIMEOUT_MAX; + + } + + CopyRect(&d->sz_ch_source, &r_client); + OffsetRect(&d->sz_ch_source, -d->sz_ch_source.left, -d->sz_ch_source.top); + CopyRect(&d->sz_ch_target, &d->r_required); + OffsetRect(&d->sz_ch_target, -d->sz_ch_target.left, -d->sz_ch_target.top); + d->sz_ch_increment = 0; + d->sz_ch_max = steps; + d->sz_ch_timeout = timeout; + d->size_changing = TRUE; + + SetTimer(hwnd, NC_TIMER_SIZER, timeout, NULL); + } + break; } /* switch(HIWORD(wParam)) */ return TRUE; } +static LRESULT nc_handle_wm_timer(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + khui_nc_wnd_data * d; + + d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); + + if (wParam == NC_TIMER_SIZER) { + + RECT r_now; + + /* are we done with this sizing operation? */ + if (!d->size_changing || + d->sz_ch_increment >= d->sz_ch_max) { + + d->size_changing = FALSE; + KillTimer(hwnd, NC_TIMER_SIZER); + return 0; + } + + /* have the requirements changed while we were processing the + sizing operation? */ + if ((d->r_required.right - d->r_required.left != + d->sz_ch_target.right) + + || + + (d->r_required.bottom - d->r_required.top != + d->sz_ch_target.bottom)) { + + /* the target size has changed. we need to restart the + sizing operation. */ + + RECT r_client; + + GetClientRect(hwnd, &r_client); + + CopyRect(&d->sz_ch_source, &r_client); + OffsetRect(&d->sz_ch_source, -d->sz_ch_source.left, -d->sz_ch_source.top); + CopyRect(&d->sz_ch_target, &d->r_required); + OffsetRect(&d->sz_ch_target, -d->sz_ch_target.left, -d->sz_ch_target.top); + d->sz_ch_increment = 0; + + /* leave the other fields alone */ + +#ifdef DEBUG + assert(d->sz_ch_max >= NC_SZ_STEPS_MIN); + assert(d->sz_ch_max <= NC_SZ_STEPS_MAX); + assert(d->sz_ch_timeout >= NC_SZ_TIMEOUT_MIN); + assert(d->sz_ch_timeout <= NC_SZ_TIMEOUT_MAX); + assert(d->size_changing); +#endif + } + + /* we are going to do the next increment */ + d->sz_ch_increment ++; + + /* now, figure out the size of the client area for this + step */ + + r_now.left = 0; + r_now.top = 0; + +#define PROPORTION(v1, v2, i, s) (((v1) * ((s) - (i)) + (v2) * (i)) / (s)) + + r_now.right = PROPORTION(d->sz_ch_source.right, d->sz_ch_target.right, + d->sz_ch_increment, d->sz_ch_max); + + r_now.bottom = PROPORTION(d->sz_ch_source.bottom, d->sz_ch_target.bottom, + d->sz_ch_increment, d->sz_ch_max); + +#undef PROPORTION + +#ifdef DEBUG + { + long dx = (r_now.right - d->sz_ch_target.right) * + (d->sz_ch_source.right - d->sz_ch_target.right); + + long dy = (r_now.bottom - d->sz_ch_target.bottom) * + (d->sz_ch_source.bottom - d->sz_ch_target.bottom); + + if (dx < 0 || dy < 0) { + KillTimer(hwnd, NC_TIMER_SIZER); + assert(dx >= 0); + assert(dy >= 0); + SetTimer(hwnd, NC_TIMER_SIZER, d->sz_ch_timeout, NULL); + } + } +#endif + + AdjustWindowRectEx(&r_now, NC_WINDOW_STYLES, FALSE, + NC_WINDOW_EX_STYLES); + + SetWindowPos(hwnd, NULL, + 0, 0, + r_now.right - r_now.left, + r_now.bottom - r_now.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOZORDER); + + /* and now we wait for the next timer message */ + + return 0; + } else if (wParam == NC_TIMER_ENABLEANIMATE) { + + d->animation_enabled = TRUE; + KillTimer(hwnd, NC_TIMER_ENABLEANIMATE); + } + + return 0; +} + +static LRESULT nc_handle_wm_notify(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + LPNMHDR nmhdr; + khui_nc_wnd_data * d; + + d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); + nmhdr = (LPNMHDR) lParam; + + if (nmhdr->code == TCN_SELCHANGE) { + /* the current tab has changed. */ + int idx; + TCITEM tcitem; + + idx = TabCtrl_GetCurSel(d->tab_wnd); + ZeroMemory(&tcitem, sizeof(tcitem)); + + tcitem.mask = TCIF_PARAM; + TabCtrl_GetItem(d->tab_wnd, idx, &tcitem); + + d->current_panel = (int) tcitem.lParam; + + nc_layout_new_cred_window(d); + + return TRUE; + } + + return FALSE; +} + static LRESULT nc_handle_wm_help(HWND hwnd, UINT uMsg, WPARAM wParam, @@ -1832,7 +2721,7 @@ static LRESULT nc_handle_wm_help(HWND hwnd, IDOK, IDH_NC_OK, IDCANCEL, IDH_NC_CANCEL, IDC_NC_HELP, IDH_NC_HELP, - IDC_NC_OPTIONS, IDH_NC_OPTIONS, + IDC_NC_ADVANCED, IDH_NC_ADVANCED, IDC_NC_CREDTEXT, IDH_NC_CREDWND, 0 }; @@ -1898,10 +2787,16 @@ static LRESULT CALLBACK nc_window_proc(HWND hwnd, case WM_COMMAND: return nc_handle_wm_command(hwnd, uMsg, wParam, lParam); + case WM_NOTIFY: + return nc_handle_wm_notify(hwnd, uMsg, wParam, lParam); + case WM_MOVE: case WM_MOVING: return nc_handle_wm_moving(hwnd, uMsg, wParam, lParam); + case WM_TIMER: + return nc_handle_wm_timer(hwnd, uMsg, wParam, lParam); + case WM_HELP: return nc_handle_wm_help(hwnd, uMsg, wParam, lParam); @@ -1925,7 +2820,7 @@ void khm_register_newcredwnd_class(void) wcx.hInstance = khm_hInstance; wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP)); wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); - wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); + wcx.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); wcx.lpszMenuName = NULL; wcx.lpszClassName = KHUI_NEWCREDWND_CLASS; wcx.hIconSm = NULL; @@ -1956,10 +2851,10 @@ HWND khm_create_newcredwnd(HWND parent, khui_new_creds * c) ARRAYLENGTH(wtitle)); } - hwnd = CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP, + hwnd = CreateWindowEx(NC_WINDOW_EX_STYLES, MAKEINTATOM(khui_newcredwnd_cls), ((c->window_title)?c->window_title: wtitle), - WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN, + NC_WINDOW_STYLES, 0,0,400,400, /* bogus values. the window is going to resize and reposition itself @@ -1988,6 +2883,6 @@ void khm_prep_newcredwnd(HWND hwnd) void khm_show_newcredwnd(HWND hwnd) { /* add all the panels in and prep UI */ - SendMessage(hwnd, KHUI_WM_NC_NOTIFY, + PostMessage(hwnd, KHUI_WM_NC_NOTIFY, MAKEWPARAM(0, WMNC_DIALOG_ACTIVATE), 0); } diff --git a/src/windows/identity/ui/newcredwnd.h b/src/windows/identity/ui/newcredwnd.h index 7813e1c19..46ac83169 100644 --- a/src/windows/identity/ui/newcredwnd.h +++ b/src/windows/identity/ui/newcredwnd.h @@ -31,32 +31,92 @@ #define KHUI_NEWCREDWND_CLASS L"KhmNewCredWnd" +typedef enum tag_nc_notification_types { + NC_NOTIFY_NONE = 0, /* no notification */ + NC_NOTIFY_MARQUEE, /* marquee type notification */ + NC_NOTIFY_PROGRESS, /* progress notification */ + NC_NOTIFY_MESSAGE, /* a message */ +} nc_notification_type; + typedef struct khui_nc_wnd_data_t { khui_new_creds * nc; + /* The tab control */ + + HWND tab_wnd; /* tab control */ + int current_panel; /* ordinal of the current panel being + displayed. */ + + /* The main panel */ HWND dlg_main; /* main dialog */ - RECT r_main; - HWND dlg_bb; /* button bar */ - RECT r_bb; - HWND dlg_ts; /* tab strip */ - RECT r_ts; + RECT r_main; /* the extent of the main panel that + we have used so far. The rect + includes the size of the area used + by the identity selector controls, + the custom controls added by + credentials providers and the + buttons that may be required when + in the mini mode. */ + RECT r_required; /* required size of the main window */ + + /* The button bar */ - khm_size ctab; /* current tab */ + HWND dlg_bb; /* button bar */ - HWND hwnd_tc_main; /* tab control button for main dialog */ + /* Sizing the new credentials window */ + + BOOL animation_enabled; /* Flag indicating whether animation + is enabled for the dialg. If this + flag is off, we don't animate size + changes even if the configuration + says so. */ + BOOL size_changing; /* flag indicating that the size of + the main window is being + adjusted. */ + RECT sz_ch_source; /* Source size, from which we are + going towards target size in + sz_ch_max steps. The RECT is self + relative (i.e. left=0 and top=0)*/ + RECT sz_ch_target; /* If we are doing an incremental size + change, this holds the target size + that we were going for. Note that + the target size might change while + we are adjusting the size. So this + helps keep track of whether we need + to start the size change again. The + RECT is self relative (i.e. left=0 + and top=0). */ + int sz_ch_increment; /* Current step of the incremental + size change operation. */ + int sz_ch_max; /* Max number of steps in the size + change operation. */ + int sz_ch_timeout; /* Milliseconds between each increment */ + + /* Custom controls and identity specifiers */ HWND hwnd_banner; /* static control for banner */ HWND hwnd_name; /* static control for name */ HWND hwnd_last_idspec; /* last identity specifier control */ - /* metrics for custom prompts and identity specifiers */ + /* Notification windows */ + + nc_notification_type notif_type; /* Type of notification */ + HWND hwnd_notif_label; /* Label for notifications */ + HWND hwnd_notif_aux; /* Other control for notifications */ + + /* Areas of the main panel */ RECT r_idspec; /* Area used by identity specifiers (relative to client) */ - RECT r_row; /* Metrics for a control row - (top=0,left=0,right=width, - bottom=height) */ + RECT r_custprompt; /* Area used by custom controls (relative + to client) */ + RECT r_notif; /* Area used for notifications. */ + + /* Metrics for custom prompts and identity specifiers */ + + RECT r_row; /* Metrics for a control row (left=0, + top=0, right=width, bottom=height) */ RECT r_area; /* Area available for controls (relative to client) */ RECT r_n_label; /* coords of the static control (relative @@ -77,10 +137,20 @@ HWND khm_create_newcredwnd(HWND parent, khui_new_creds * c); void khm_prep_newcredwnd(HWND hwnd); void khm_show_newcredwnd(HWND hwnd); +/* Width of the button bar in dialog units */ +#define NCDLG_BBAR_WIDTH 66 +/* Height of the button bar in dialog units */ +#define NCDLG_BBAR_HEIGHT 181 + +/* Control identifier for the tab control in the new credentials + dialog. We declare this here since we will be creating the control + manually. */ +#define IDC_NC_TABS 8001 + /* This is the first control ID that is created in the custom tabstrip control buttons. Subsequent buttons get consecutive IDs starting from this one. */ -#define NC_TS_CTRL_ID_MIN 8001 +#define NC_TS_CTRL_ID_MIN 8002 /* Maximum number of controls */ #define NC_TS_MAX_CTRLS 8 @@ -100,4 +170,20 @@ void khm_show_newcredwnd(HWND hwnd); /* the maximum control ID that may be used by an identity provider */ #define NC_IS_CTRL_ID_MAX (NC_IS_CTRL_ID_MIN + NC_IS_MAX_CTRLS - 1) +#define NC_WINDOW_EX_STYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP) +#define NC_WINDOW_STYLES (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN) + +#define NC_SZ_STEPS_MIN 3 +#define NC_SZ_STEPS_DEF 10 +#define NC_SZ_STEPS_MAX 100 + +#define NC_SZ_TIMEOUT_MIN 5 +#define NC_SZ_TIMEOUT_DEF 10 +#define NC_SZ_TIMEOUT_MAX 500 + +#define NC_TIMER_SIZER 1001 +#define NC_TIMER_ENABLEANIMATE 1002 + +#define ENABLEANIMATE_TIMEOUT 400 + #endif diff --git a/src/windows/identity/ui/notifier.c b/src/windows/identity/ui/notifier.c index 9804abfae..2d020bde9 100644 --- a/src/windows/identity/ui/notifier.c +++ b/src/windows/identity/ui/notifier.c @@ -17,7 +17,7 @@ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * ACTION OF CONTRACT TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ @@ -31,13 +31,50 @@ #define KHUI_NOTIFIER_CLASS L"KhuiNotifierMsgWindowClass" #define KHUI_ALERTER_CLASS L"KhuiAlerterWindowClass" +#define KHUI_ALERTBIN_CLASS L"KhuiAlertBinWindowClass" #define KHUI_NOTIFIER_WINDOW L"KhuiNotifierMsgWindow" /* notifier message for notification icon */ #define KHUI_WM_NOTIFIER WM_COMMAND -#define KHUI_ALERT_QUEUE_MAX 64 +#define DRAWTEXTOPTIONS (DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK) + +/* are we showing an alert? */ +#define ALERT_DISPLAYED() (balloon_alert != NULL || khui_alert_windows != NULL) + +/* Forward declarations */ + +struct tag_alerter_wnd_data; +typedef struct tag_alerter_wnd_data alerter_wnd_data; + +struct tag_alert_list; +typedef struct tag_alert_list alert_list; + +static khm_int32 +alert_show(khui_alert * a); + +static khm_int32 +alert_show_minimized(khui_alert * a); + +static khm_int32 +alert_show_normal(khui_alert * a); + +static khm_int32 +alert_show_list(alert_list * alist); + +static khm_int32 +alert_enqueue(khui_alert * a); + +static void +check_for_queued_alerts(void); + +static khm_int32 +alert_consolidate(alert_list * alist, + khui_alert * alert, + khm_boolean add_from_queue); + +/* Globals */ /* window class registration atom for message only notifier window class */ @@ -45,35 +82,57 @@ ATOM atom_notifier = 0; /* window class registration atom for alert windows */ ATOM atom_alerter = 0; +/* window class registration atom for the alert "bin", which is the + window that holds all the alerts. */ +ATOM atom_alert_bin = 0; /* notifier message window */ HWND hwnd_notifier = NULL; BOOL notifier_ready = FALSE; -khm_boolean notifier_modal_loop = FALSE; +/* The list of alert windows currently active */ +alerter_wnd_data * khui_alert_windows = NULL; -khui_alert * current_alert = NULL; +/* Notification icon for when there are no alerts to be displayed */ +int iid_normal = IDI_NOTIFY_NONE; + +/* The alert currently being displayed in a balloon */ +khui_alert * balloon_alert; + +/********************************************************************** + Alert Queue + + The alert queue is the data structure that keeps track of all the + alerts that are waiting to be displayed. Alerts will be placed on + the queue if they cannot be immediately displayed for some reason + (e.g. another alert is being displayed, or the user is working in + another window). +***********************************************************************/ + +#define KHUI_ALERT_QUEUE_MAX 64 khui_alert * alert_queue[KHUI_ALERT_QUEUE_MAX]; khm_int32 alert_queue_head = 0; khm_int32 alert_queue_tail = 0; -int iid_normal = IDI_NOTIFY_NONE; - #define is_alert_queue_empty() (alert_queue_head == alert_queue_tail) #define is_alert_queue_full() (((alert_queue_tail + 1) % KHUI_ALERT_QUEUE_MAX) == alert_queue_head) +/* NOTE: the alert queue functions are unsafe to call from any thread + other than the UI thread. */ + static void -add_to_alert_queue(khui_alert * a) { +alert_queue_put_alert(khui_alert * a) { if (is_alert_queue_full()) return; alert_queue[alert_queue_tail++] = a; khui_alert_hold(a); alert_queue_tail %= KHUI_ALERT_QUEUE_MAX; } +/* the caller needs to release the alert that's returned */ static khui_alert * -del_from_alert_queue(void) { +alert_queue_get_alert(void) { khui_alert * a; if (is_alert_queue_empty()) return NULL; @@ -83,57 +142,140 @@ del_from_alert_queue(void) { return a; /* held */ } -static khui_alert * -peek_alert_queue(void) { - if (is_alert_queue_empty()) return NULL; - return alert_queue[alert_queue_head]; +static int +alert_queue_get_size(void) { + if (is_alert_queue_empty()) + return 0; + + if (alert_queue_tail < alert_queue_head) { + return (alert_queue_tail + KHUI_ALERT_QUEUE_MAX - alert_queue_head); + } else { + return alert_queue_tail - alert_queue_head; + } } -static void -check_for_queued_alerts(void) { - if (!is_alert_queue_empty()) { - khui_alert * a; +static khui_alert * +alert_queue_get_alert_by_pos(int pos) { + khui_alert * a; - a = peek_alert_queue(); + if (is_alert_queue_empty() || + pos >= alert_queue_get_size() || + pos < 0) { + return NULL; + } - if (a->title) { - HICON hi; - int res; + a = alert_queue[(alert_queue_head + pos) % KHUI_ALERT_QUEUE_MAX]; + if (a) { + khui_alert_hold(a); + } + return a; +} - if (a->severity == KHERR_ERROR) - res = OIC_ERROR; - else if (a->severity == KHERR_WARNING) - res = OIC_WARNING; - else - res = OIC_INFORMATION; +static int +alert_queue_delete_alert(khui_alert * a) { + int idx; + int succ; - hi = LoadImage(0, MAKEINTRESOURCE(res), - IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - LR_SHARED); + idx = alert_queue_head; + while(idx != alert_queue_tail) { + if (alert_queue[idx] == a) + break; - khm_statusbar_set_part(KHUI_SBPART_NOTICE, - hi, - a->title); - } - } else { - khm_statusbar_set_part(KHUI_SBPART_NOTICE, - NULL, NULL); + idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX; + } + + if (idx == alert_queue_tail) + return 0; + +#ifdef DEBUG + assert(alert_queue[idx]); +#endif + khui_alert_release(alert_queue[idx]); + + succ = (idx + 1) % KHUI_ALERT_QUEUE_MAX; + while(succ != alert_queue_tail) { + alert_queue[idx] = alert_queue[succ]; + + succ = (succ + 1) % KHUI_ALERT_QUEUE_MAX; + idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX; } + + alert_queue_tail = idx; + return 1; } +/* the caller needs to release the alert that's returned */ +static khui_alert * +alert_queue_peek(void) { + khui_alert * a; -/* forward dcls */ -static khm_int32 -alert_show(khui_alert * a); + if (is_alert_queue_empty()) + return NULL; -static khm_int32 -alert_show_minimized(khui_alert * a); + a = alert_queue[alert_queue_head]; + khui_alert_hold(a); -static khm_int32 -alert_show_normal(khui_alert * a); + return a; +} + +/********************************************************************** + Alert List + + A list of alerts. Currently has a fixed upper limit, but the limit + is high enough for now. +***********************************************************************/ + +typedef struct tag_alert_list { + khui_alert * alerts[KHUI_ALERT_QUEUE_MAX]; + int n_alerts; + wchar_t title[KHUI_MAXCCH_TITLE]; +} alert_list; + +static void +alert_list_init(alert_list * alist) { + ZeroMemory(alist, sizeof(*alist)); +} + +static void +alert_list_set_title(alert_list * alist, wchar_t * title) { + StringCbCopy(alist->title, sizeof(alist->title), title); +} static khm_int32 -alert_enqueue(khui_alert * a); +alert_list_add_alert(alert_list * alist, + khui_alert * alert) { + + if (alist->n_alerts == ARRAYLENGTH(alist->alerts)) + return KHM_ERROR_NO_RESOURCES; + + khui_alert_hold(alert); + alist->alerts[alist->n_alerts++] = alert; + + return KHM_ERROR_SUCCESS; +} + +static void +alert_list_destroy(alert_list * alist) { + int i; + + for (i=0; i < alist->n_alerts; i++) { + if (alist->alerts[i] != NULL) { + khui_alert_release(alist->alerts[i]); + alist->alerts[i] = NULL; + } + } + + alist->n_alerts = 0; +} + + +/********************************************************************** + Notifier Window + + The notifier window manages the notification icon and handles + KMSG_ALERT messages sent from the UI library. The window will exist + for the lifetime of the application. +***********************************************************************/ /* These are defined for APPVER >= 0x501. We are defining them here so that we can build with APPVER = 0x500 and use the same binaries @@ -156,15 +298,6 @@ alert_enqueue(khui_alert * a); #endif -/********************************************************************** - Notifier -*********************************************************************** - -The notifier is a message only window that listens for notifier -messages. This window will exist for the lifetime of the application -and will use alerter windows as needed to show application alerts. -*/ - static LRESULT CALLBACK notifier_wnd_proc(HWND hwnd, UINT uMsg, @@ -182,13 +315,29 @@ notifier_wnd_proc(HWND hwnd, /* handle notifier messages */ switch(m->subtype) { case KMSG_ALERT_SHOW: - rv = alert_show((khui_alert *) m->vparam); - khui_alert_release((khui_alert *) m->vparam); + { + khui_alert * a; + + a = (khui_alert *) m->vparam; +#ifdef DEBUG + assert(a != NULL); +#endif + rv = alert_show(a); + khui_alert_release(a); + } break; case KMSG_ALERT_QUEUE: - rv = alert_enqueue((khui_alert *) m->vparam); - khui_alert_release((khui_alert *) m->vparam); + { + khui_alert * a; + + a = (khui_alert *) m->vparam; +#ifdef DEBUG + assert(a != NULL); +#endif + rv = alert_enqueue(a); + khui_alert_release(a); + } break; case KMSG_ALERT_CHECK_QUEUE: @@ -196,15 +345,42 @@ notifier_wnd_proc(HWND hwnd, break; case KMSG_ALERT_SHOW_QUEUED: - if (current_alert == NULL) { - khui_alert * a; - - a = del_from_alert_queue(); - if (a) { - rv = alert_show(a); - check_for_queued_alerts(); - khui_alert_release(a); + if (!ALERT_DISPLAYED()) { + + /* show next consolidated batch */ + alert_list alist; + int n; + + alert_list_init(&alist); + n = alert_consolidate(&alist, NULL, TRUE); + + if (n) { + if (n == 1) { + khui_alert_lock(alist.alerts[0]); + + if (alist.alerts[0]->title) { + alert_list_set_title(&alist, alist.alerts[0]->title); + } else { + wchar_t title[KHUI_MAXCCH_TITLE]; + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + title, ARRAYLENGTH(title)); + alert_list_set_title(&alist, title); + } + + khui_alert_unlock(alist.alerts[0]); + } else { + wchar_t title[KHUI_MAXCCH_TITLE]; + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + title, ARRAYLENGTH(title)); + alert_list_set_title(&alist, title); + } + + alert_show_list(&alist); } + + alert_list_destroy(&alist); + + check_for_queued_alerts(); } break; @@ -213,15 +389,20 @@ notifier_wnd_proc(HWND hwnd, khui_alert * a; a = (khui_alert *) m->vparam; +#ifdef DEBUG + assert(a != NULL); +#endif + khui_alert_lock(a); a->flags |= KHUI_ALERT_FLAG_MODAL; + khui_alert_unlock(a); + rv = alert_show(a); - khui_alert_release(a); if (KHM_SUCCEEDED(rv)) { - notifier_modal_loop = TRUE; - - khm_message_loop_int(¬ifier_modal_loop); + khm_message_loop_int(&a->displayed); } + + khui_alert_release(a); } break; } @@ -276,26 +457,40 @@ notifier_wnd_proc(HWND hwnd, break; case NIN_BALLOONUSERCLICK: - if (current_alert) { - if ((current_alert->flags & KHUI_ALERT_FLAG_DEFACTION) && - current_alert->n_alert_commands > 0) { + if (balloon_alert) { + khm_notify_icon_change(KHERR_NONE); + + khui_alert_lock(balloon_alert); + + if ((balloon_alert->flags & KHUI_ALERT_FLAG_DEFACTION) && + (balloon_alert->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON) && + balloon_alert->n_alert_commands > 0) { PostMessage(khm_hwnd_main, WM_COMMAND, - MAKEWPARAM(current_alert->alert_commands[0], + MAKEWPARAM(balloon_alert->alert_commands[0], 0), 0); - } else if (current_alert->flags & + } else if (balloon_alert->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) { khm_show_main_window(); - alert_show_normal(current_alert); + alert_show_normal(balloon_alert); } + + khui_alert_unlock(balloon_alert); + khui_alert_release(balloon_alert); + balloon_alert = NULL; + } else { +#ifdef DEBUG + assert(FALSE); +#endif } - /* fallthrough */ + break; + case NIN_BALLOONHIDE: case NIN_BALLOONTIMEOUT: khm_notify_icon_change(KHERR_NONE); - if (current_alert) { - khui_alert_release(current_alert); - current_alert = NULL; + if (balloon_alert) { + khui_alert_release(balloon_alert); + balloon_alert = NULL; } break; } @@ -342,211 +537,1162 @@ khm_register_notifier_wnd_class(void) Alerter **********************************************************************/ -typedef struct tag_alerter_wnd_data { +typedef struct tag_alerter_alert_data { khui_alert * alert; + BOOL seen; /* has the user seen this alert? */ + + BOOL has_commands; /* we cache the value here. otherwise + we'll have to get a lock on the + alert each time we have to find out + whether there are any commands for + this alert. */ + + RECT r_alert; /* the entire alert, relative to self. */ + + /* the following rects are relative to the top left of r_alert. */ + + RECT r_title; /* the title. deflate by padding to + get the text rect. */ + RECT r_icon; /* rect for icon */ + RECT r_message; /* rect for the text. no padding + necessary. */ + RECT r_suggestion; /* rect for the suggestion. deflate + by padding to get the suggestion + rect. The suggestion rect includes + space for the small icon on the + left and padding between the icon + and the text. The size of the small + icon are as per system metrics + SM_C{X,Y}SMICON. Padding is + s_pad.cx vertical. */ + + int n_cmd_buttons; /* number of command buttons in this alert. */ + + RECT r_buttons[KHUI_MAX_ALERT_COMMANDS]; + /* rects for the command buttons. */ + + HWND hwnd_buttons[KHUI_MAX_ALERT_COMMANDS]; + /* handles for the command buttons */ + + LDCL(struct tag_alerter_alert_data); +} alerter_alert_data; + +typedef struct tag_alerter_wnd_data { HWND hwnd; HFONT hfont; - BOOL metrics_done; + wchar_t caption[KHUI_MAXCCH_TITLE]; /* the original + caption for the + dialog. */ - HWND hwnd_buttons[KHUI_MAX_ALERT_COMMANDS]; + HWND hw_bin; + HWND hw_scroll; + HWND hw_close; - /* various metrics */ + int scroll_top; - /* calculated during WM_CREATE and adjusted during WM_PAINT */ - int dy_message; - int dy_suggestion; + int n_cmd_buttons; /* total number of command buttons + in all the alerts being shown in + this dialog. */ + /* various metrics */ /* calculated during WM_CREATE */ - int dx_button; - int dy_button; - int dx_button_incr; - int dx_margin; - int dy_margin; - int dy_bb; - int x_message; - int dx_message; - int dx_icon; - int dy_icon; - int dx_suggest_pad; - - /* calculated during WM_CREATE and adjusted during WM_PAINT */ - int dx_client; - int dy_client; - - /* calculated during WM_PAINT */ - int y_message; - int y_suggestion; - - LDCL(struct tag_alerter_wnd_data); -} alerter_wnd_data; + SIZE s_button; + SIZE s_margin; + RECT r_text; /* only .left and .right are used. rest are 0 */ + RECT r_title; /* only .left, .right and .bottom are used. .top=0 */ + SIZE s_icon; + SIZE s_pad; -alerter_wnd_data * khui_alerts = NULL; + int cx_wnd; + int cy_max_wnd; + + /* derived from the alert sizes */ + SIZE s_alerts; + + QDCL(alerter_alert_data); /* queue of alerts that are being + shown in this window. */ + + LDCL(struct tag_alerter_wnd_data); /* for adding to + khui_alert_windows list. */ + + int n_alerts; + +} alerter_wnd_data; #define NTF_PARAM DWLP_USER /* dialog sizes in base dialog units */ -#define NTF_MARGIN 5 -#define NTF_WIDTH 200 +#define NTF_MARGIN 5 +#define NTF_WIDTH 200 +#define NTF_MAXHEIGHT 150 -#define NTF_BB_HEIGHT 15 +#define NTF_TITLE_X NTF_MARGIN +#define NTF_TITLE_WIDTH (NTF_WIDTH - NTF_MARGIN*2) +#define NTF_TITLE_HEIGHT 10 -#define NTF_ICON_X NTF_MARGIN -#define NTF_ICON_WIDTH 20 -#define NTF_ICON_HEIGHT 20 +#define NTF_ICON_WIDTH 20 +#define NTF_ICON_HEIGHT 20 -#define NTF_MSG_X (NTF_ICON_X + NTF_ICON_WIDTH + NTF_MARGIN) -#define NTF_MSG_WIDTH ((NTF_WIDTH - NTF_MARGIN) - NTF_MSG_X) -#define NTF_MSG_HEIGHT 15 +#define NTF_TEXT_X (NTF_MARGIN + NTF_ICON_WIDTH + NTF_MARGIN) +#define NTF_TEXT_WIDTH ((NTF_WIDTH - NTF_MARGIN) - NTF_TEXT_X) +#define NTF_TEXT_PAD 2 -#define NTF_SUG_X NTF_MSG_X -#define NTF_SUG_WIDTH NTF_MSG_WIDTH -#define NTF_SUG_HEIGHT NTF_MSG_HEIGHT -#define NTF_SUG_PAD 2 +#define NTF_BUTTON_WIDTH ((NTF_TEXT_WIDTH - (KHUI_MAX_ALERT_COMMANDS - 1)*NTF_MARGIN) / KHUI_MAX_ALERT_COMMANDS) +#define NTF_BUTTON_HEIGHT 15 -#define NTF_BUTTON_X NTF_MSG_X +#define NTF_TIMEOUT 20000 -#define NTF_BUTTON_WIDTH ((NTF_MSG_WIDTH - 3*NTF_MARGIN) / 4) -#define NTF_BUTTON_XINCR (NTF_BUTTON_WIDTH + NTF_MARGIN) -#define NTF_BUTTON_HEIGHT (NTF_BB_HEIGHT - NTF_MARGIN) +#define ALERT_WINDOW_EX_SYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP) +#define ALERT_WINDOW_STYLES (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN) -#define NTF_TIMEOUT 20000 +/* Control ids */ +#define IDC_NTF_ALERTBIN 998 +#define IDC_NTF_CLOSE 999 -static khm_int32 -alert_show_minimized(khui_alert * a) { - wchar_t tbuf[64]; - wchar_t mbuf[256]; +#define IDC_NTF_CMDBUTTONS 1001 +#define IDC_FROM_IDX(alert, bn) ((alert) * KHUI_MAX_ALERT_COMMANDS + (bn) + IDC_NTF_CMDBUTTONS) +#define ALERT_FROM_IDC(idc) (((idc) - IDC_NTF_CMDBUTTONS) / KHUI_MAX_ALERT_COMMANDS) +#define BUTTON_FROM_IDC(idc) (((idc) - IDC_NTF_CMDBUTTONS) % KHUI_MAX_ALERT_COMMANDS) - if (a->message == NULL) - return KHM_ERROR_SUCCESS; +/* if the only command in an alert is "Close", we assume that the + alert has no commands. */ +#define ALERT_HAS_CMDS(a) ((a)->n_alert_commands > 1 || ((a)->n_alert_commands == 1 && (a)->alert_commands[0] != KHUI_PACTION_CLOSE)) - if (a->title == NULL) { - LoadString(khm_hInstance, IDS_ALERT_DEFAULT, - tbuf, ARRAYLENGTH(tbuf)); - } else { - StringCbCopy(tbuf, sizeof(tbuf), a->title); - } +static void +add_alert_to_wnd_data(alerter_wnd_data * d, + khui_alert * a) { + alerter_alert_data * adata; - if (FAILED(StringCbCopy(mbuf, sizeof(mbuf), a->message)) || - (!(a->flags & KHUI_ALERT_FLAG_DEFACTION) && - (a->n_alert_commands > 0 || - a->suggestion || - (a->flags & KHUI_ALERT_FLAG_VALID_ERROR)))) { - /* if mbuf wasn't big enough, this should have copied a - truncated version of it */ - size_t cch_m, cch_p; - wchar_t postfix[256]; + adata = PMALLOC(sizeof(*adata)); + ZeroMemory(adata, sizeof(*adata)); - cch_p = LoadString(khm_hInstance, IDS_ALERT_MOREINFO, postfix, - ARRAYLENGTH(postfix)); - cch_p++; /* account for NULL */ + adata->alert = a; + khui_alert_hold(a); - StringCchLength(mbuf, ARRAYLENGTH(mbuf), &cch_m); - cch_m = min(cch_m, ARRAYLENGTH(mbuf) - cch_p); + khui_alert_lock(a); - StringCchCopy(mbuf + cch_m, ARRAYLENGTH(mbuf) - cch_m, - postfix); + a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW; - a->flags |= KHUI_ALERT_FLAG_REQUEST_WINDOW; + a->displayed = TRUE; + + khui_alert_unlock(a); + + QPUT(d, adata); +} + +static alerter_wnd_data * +create_alerter_wnd_data(HWND hwnd, alert_list * l) { + alerter_wnd_data * d; + int i; + LONG dlgb; + + d = PMALLOC(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + + d->hwnd = hwnd; + + GetWindowText(hwnd, d->caption, ARRAYLENGTH(d->caption)); + + for (i=0; i < l->n_alerts; i++) { + add_alert_to_wnd_data(d, l->alerts[i]); } - a->flags |= KHUI_ALERT_FLAG_DISPLAY_BALLOON; + d->n_alerts = l->n_alerts; -#if (_WIN32_IE >= 0x0501) - current_alert = a; - khui_alert_hold(a); -#endif + LPUSH(&khui_alert_windows, d); - khm_notify_icon_balloon(a->severity, - tbuf, - mbuf, - NTF_TIMEOUT); + /* Compute a few metrics first */ - return KHM_ERROR_SUCCESS; + dlgb = GetDialogBaseUnits(); + +#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4) +#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8) + + d->cx_wnd = DLG2SCNX(NTF_WIDTH); + d->cy_max_wnd = DLG2SCNY(NTF_MAXHEIGHT); + + d->s_button.cx = DLG2SCNX(NTF_BUTTON_WIDTH); + d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT); + + d->s_margin.cx = DLG2SCNX(NTF_MARGIN); + d->s_margin.cy = DLG2SCNY(NTF_MARGIN); + + d->r_text.left = DLG2SCNX(NTF_TEXT_X); + d->r_text.right = DLG2SCNX(NTF_TEXT_WIDTH + NTF_TEXT_X); + d->r_text.top = 0; + d->r_text.bottom = 0; + + d->r_title.left = DLG2SCNX(NTF_TITLE_X); + d->r_title.right = DLG2SCNX(NTF_TITLE_X + NTF_TITLE_WIDTH); + d->r_title.top = 0; + d->r_title.bottom = DLG2SCNY(NTF_TITLE_HEIGHT); + + d->s_icon.cx = DLG2SCNX(NTF_ICON_WIDTH); + d->s_icon.cy = DLG2SCNY(NTF_ICON_HEIGHT); + + d->s_pad.cx = DLG2SCNX(NTF_TEXT_PAD); + d->s_pad.cy = DLG2SCNY(NTF_TEXT_PAD); + +#undef DLG2SCNX +#undef DLG2SCNY + + return d; } -static khm_int32 -alert_show_normal(khui_alert * a) { - HWND hwa; - wchar_t buf[256]; - wchar_t * title; +static void +layout_alert(HDC hdc, alerter_wnd_data * d, + alerter_alert_data * adata) { + RECT r; + size_t len; + int y; + int icon_y; - if(a->title == NULL) { - LoadString(khm_hInstance, IDS_ALERT_DEFAULT, - buf, ARRAYLENGTH(buf)); - title = buf; - } else - title = a->title; +#ifdef DEBUG + assert(adata->alert); +#endif - /* if we don't have any commands, we just add a "close" button */ - if (a->n_alert_commands == 0) { - khui_alert_add_command(a, KHUI_PACTION_CLOSE); - } + khui_alert_lock(adata->alert); + + y = 0; + + /* Title */ + + y += d->s_margin.cy; + + if (adata->alert->title && + wcscmp(adata->alert->title, d->caption)) { + + CopyRect(&adata->r_title, &d->r_title); + OffsetRect(&adata->r_title, 0, y); + + y = adata->r_title.bottom + d->s_margin.cy; + + } else { + + SetRectEmpty(&adata->r_title); - /* if there are other alerts queued up, we should add a 'Next - alert...' button that when clicked, would show the next queued - alert. However, we only do this if the current alert doesn't - actually require a command response. Otherwise, clicking the - 'next' button will be the equivalent of cancelling out of the - alert without selecting any of the commands. */ - if (!is_alert_queue_empty() && - a->n_alert_commands == 1 && - a->alert_commands[0] == KHUI_PACTION_CLOSE) { - - khui_alert_add_command(a, KHUI_PACTION_NEXT); } - /* we don't need to keep track of the window handle + /* Icon */ + + SetRect(&adata->r_icon, d->s_margin.cx, y, + d->s_margin.cx + d->s_icon.cx, + y + d->s_icon.cy); + + icon_y = adata->r_icon.bottom + d->s_margin.cy; /* the bottom of the icon */ + + /* Message */ + + if (adata->alert->message && + SUCCEEDED(StringCchLength(adata->alert->message, + KHUI_MAXCCH_MESSAGE, + &len))) { + + CopyRect(&r, &d->r_text); + + DrawTextEx(hdc, adata->alert->message, (int) len, + &r, + DRAWTEXTOPTIONS, + NULL); + + OffsetRect(&r, 0, y); + CopyRect(&adata->r_message, &r); + + y = r.bottom + d->s_margin.cy; + + } else { + + SetRectEmpty(&adata->r_message); + + } + + /* Suggestion */ + + if (adata->alert->suggestion && + SUCCEEDED(StringCchLength(adata->alert->suggestion, + KHUI_MAXCCH_SUGGESTION, + &len))) { + int pad = d->s_pad.cx + GetSystemMetrics(SM_CXSMICON); + + CopyRect(&r, &d->r_text); + r.left += pad; + + DrawTextEx(hdc, adata->alert->suggestion, (int) len, + &r, + DRAWTEXTOPTIONS, + NULL); + + r.left -= pad; + + InflateRect(&r, d->s_pad.cx, d->s_pad.cy); + OffsetRect(&r, 0, -r.top + y); + CopyRect(&adata->r_suggestion, &r); + + y = r.bottom + d->s_margin.cy; + + } else { + + SetRectEmpty(&adata->r_suggestion); + + } + + y = max(y, icon_y); + + /* Buttons */ + + if (ALERT_HAS_CMDS(adata->alert)) { + khm_int32 i; + int x; + + adata->has_commands = TRUE; + + x = d->r_text.left; + +#ifdef DEBUG + assert(adata->alert->n_alert_commands <= KHUI_MAX_ALERT_COMMANDS); +#endif + + for (i=0; i < adata->alert->n_alert_commands; i++) { + SetRect(&adata->r_buttons[i], x, y, x + d->s_button.cx, y + d->s_button.cy); + + x += d->s_button.cx + d->s_margin.cx; + } + + y += d->s_button.cy + d->s_margin.cy; + } + + khui_alert_unlock(adata->alert); + + /* Now set the rect for the whole alert */ + SetRect(&adata->r_alert, 0, 0, d->cx_wnd, y); + +} + +static void +estimate_alerter_wnd_sizes(alerter_wnd_data * d) { + HDC hdc; + HFONT hf_old; + int height = 0; + + alerter_alert_data * adata; + + hdc = GetDC(d->hwnd); +#ifdef DEBUG + assert(hdc); +#endif + + if (d->hfont == NULL) + d->hfont = (HFONT) GetStockObject(DEFAULT_GUI_FONT); + +#ifdef DEBUG + assert(d->hfont); +#endif + + hf_old = SelectFont(hdc, d->hfont); + + adata = QTOP(d); + while(adata) { + layout_alert(hdc, d, adata); + + height += adata->r_alert.bottom; + + adata = QNEXT(adata); + } + + SelectFont(hdc, hf_old); + ReleaseDC(d->hwnd, hdc); + + d->s_alerts.cx = d->cx_wnd; + d->s_alerts.cy = height; +} + +static void +layout_command_buttons(alerter_wnd_data * d) { + + alerter_alert_data * adata; + HDWP hdefer; + int y; + + hdefer = BeginDeferWindowPos(d->n_cmd_buttons); + + y = 0; + adata = QTOP(d); + while (adata) { + RECT r; + int i; + + if (!adata->has_commands) + goto done; + + for (i=0; i < adata->n_cmd_buttons; i++) { + if (IsRectEmpty(&adata->r_buttons[i]) || + adata->hwnd_buttons[i] == NULL) { +#ifdef DEBUG + assert(FALSE); +#endif + continue; + } + + CopyRect(&r, &adata->r_buttons[i]); + OffsetRect(&r, 0, y - d->scroll_top); + + DeferWindowPos(hdefer, + adata->hwnd_buttons[i], NULL, + r.left, r.top, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | + SWP_NOSIZE); + } + + done: + y += adata->r_alert.bottom; + adata = QNEXT(adata); + } + + EndDeferWindowPos(hdefer); +} + +static void +setup_alerter_window_controls(alerter_wnd_data * d) { + + RECT r_alerts; + RECT r_window; + RECT r_client; + RECT r_parent; + HWND hw_parent; + BOOL close_button = FALSE; + BOOL scrollbar = FALSE; + BOOL redraw_scollbar = FALSE; + + /* estimate_alerter_wnd_sizes() must be called before calling + this. */ +#ifdef DEBUG + assert(d->s_alerts.cy > 0); +#endif + + r_alerts.left = 0; + r_alerts.top = 0; + r_alerts.right = d->cx_wnd; + + if (d->s_alerts.cy > d->cy_max_wnd) { + + BOOL redraw = FALSE; + + r_alerts.right += GetSystemMetrics(SM_CXVSCROLL); + r_alerts.bottom = d->cy_max_wnd; + + CopyRect(&r_client, &r_alerts); + r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy; + close_button = TRUE; + + if (d->scroll_top > d->s_alerts.cy - d->cy_max_wnd) + d->scroll_top = d->s_alerts.cy - d->cy_max_wnd; + + scrollbar = TRUE; + } else { + r_alerts.bottom = d->s_alerts.cy; + + CopyRect(&r_client, &r_alerts); + + if (d->n_alerts == 1) { + + if (!QTOP(d)->has_commands) { + r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy; + close_button = TRUE; + } + + } else { + + r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy; + close_button = TRUE; + } + + d->scroll_top = 0; + } + + if (d->hw_bin == NULL) { + d->hw_bin = CreateWindowEx(WS_EX_CONTROLPARENT, + MAKEINTATOM(atom_alert_bin), + L"", + WS_CHILD | WS_CLIPCHILDREN | + WS_VISIBLE | + ((scrollbar)? WS_VSCROLL : 0), + r_alerts.left, r_alerts.top, + r_alerts.right - r_alerts.left, + r_alerts.bottom - r_alerts.top, + d->hwnd, + (HMENU) IDC_NTF_ALERTBIN, + khm_hInstance, + (LPVOID) d); + } else { + redraw_scollbar = TRUE; + SetWindowLongPtr(d->hw_bin, GWL_STYLE, + WS_CHILD | WS_CLIPCHILDREN | + WS_VISIBLE | + ((scrollbar)? WS_VSCROLL : 0)); + SetWindowPos(d->hw_bin, NULL, + r_alerts.left, r_alerts.top, + r_alerts.right - r_alerts.left, + r_alerts.bottom - r_alerts.top, + SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE); + } + + if (scrollbar) { + SCROLLINFO si; + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; + si.nMin = 0; + si.nMax = d->s_alerts.cy; + si.nPage = d->cy_max_wnd; + si.nPos = d->scroll_top; + + SetScrollInfo(d->hw_bin, SB_VERT, &si, redraw_scollbar); + } + + /* create the action buttons */ + { + alerter_alert_data * adata; + int y; + int idx; + HWND last_window = HWND_TOP; + int n_buttons = 0; + + idx = 0; + y = - d->scroll_top; + adata = QTOP(d); + while(adata) { + if (adata->has_commands) { + int i; + wchar_t caption[KHUI_MAXCCH_SHORT_DESC]; + khui_action * action; + RECT r; + + khui_alert_lock(adata->alert); + + adata->n_cmd_buttons = adata->alert->n_alert_commands; + + for (i=0; i < adata->alert->n_alert_commands; i++) { + + n_buttons ++; + + if (adata->hwnd_buttons[i] != NULL) { + /* already there */ + CopyRect(&r, &adata->r_buttons[i]); + OffsetRect(&r, 0, y); + + SetWindowPos(adata->hwnd_buttons[i], last_window, + r.left, r.top, + r.right - r.left, + r.bottom - r.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_SHOWWINDOW); + + last_window = adata->hwnd_buttons[i]; + + continue; + } + + action = khui_find_action(adata->alert->alert_commands[i]); + + if (action == NULL) { +#ifdef DEBUG + assert(FALSE); +#endif + continue; + } + + if (action->caption) + StringCbCopy(caption, sizeof(caption), action->caption); + else if (action->is_caption) + LoadString(khm_hInstance, action->is_caption, caption, + ARRAYLENGTH(caption)); + else { +#ifdef DEBUG + assert(FALSE); +#endif + caption[0] = L'\0'; + } + + CopyRect(&r, &adata->r_buttons[i]); + OffsetRect(&r, 0, y); + + adata->hwnd_buttons[i] = + CreateWindowEx(0, + L"BUTTON", + caption, + WS_CHILD | WS_TABSTOP, + r.left, r.top, + r.right - r.left, + r.bottom - r.top, + d->hw_bin, + (HMENU) (INT_PTR) IDC_FROM_IDX(idx, i), + khm_hInstance, + NULL); +#ifdef DEBUG + assert(adata->hwnd_buttons[i]); +#endif + SetWindowPos(adata->hwnd_buttons[i], last_window, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + last_window = adata->hwnd_buttons[i]; + } + + khui_alert_unlock(adata->alert); + } + + y += adata->r_alert.bottom; + adata = QNEXT(adata); + } + + d->n_cmd_buttons = n_buttons; + } + + if (close_button) { + if (d->hw_close == NULL) { + khui_action * close_action; + wchar_t caption[256]; + + close_action = khui_find_action(KHUI_PACTION_CLOSE); +#ifdef DEBUG + assert(close_action); +#endif + if (close_action->caption) + StringCbCopy(caption, sizeof(caption), close_action->caption); + else if (close_action->is_caption) + LoadString(khm_hInstance, close_action->is_caption, caption, + ARRAYLENGTH(caption)); + else { +#ifdef DEBUG + assert(FALSE); +#endif + caption[0] = L'\0'; + } + + d->hw_close = CreateWindowEx(0, + L"BUTTON", + caption, + WS_CHILD | BS_DEFPUSHBUTTON, + 0,0,100,100, + d->hwnd, + (HMENU) IDC_NTF_CLOSE, + khm_hInstance, + NULL); + +#ifdef DEBUG + assert(d->hw_close); + assert(d->hfont); +#endif + if (d->hfont) + SendMessage(d->hw_close, WM_SETFONT, (WPARAM) d->hfont, FALSE); + } + + { + int x,y,width,height; + + x = d->r_text.left; + y = r_client.bottom - (d->s_margin.cy + d->s_button.cy); + width = d->s_button.cx; + height = d->s_button.cy; + + SetWindowPos(d->hw_close, NULL, + x, y, width, height, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | + SWP_SHOWWINDOW); + } + } + + CopyRect(&r_window, &r_client); + AdjustWindowRectEx(&r_window, ALERT_WINDOW_STYLES, + FALSE, ALERT_WINDOW_EX_SYLES); + OffsetRect(&r_window, -r_window.left, -r_window.top); + + /* center the window above the parent window. */ + + hw_parent = GetWindow(d->hwnd, GW_OWNER); + GetWindowRect(hw_parent, &r_parent); + + { + int x,y; + + x = (r_parent.left + r_parent.right - (r_window.right - r_window.left)) / 2; + y = (r_parent.top + r_parent.bottom - (r_window.bottom - r_window.top)) / 2; + + SetWindowPos(d->hwnd, + HWND_TOP, + x, y, + r_window.right - r_window.left, + r_window.bottom - r_window.top, + SWP_SHOWWINDOW | SWP_NOOWNERZORDER); + + } +} + +static void +destroy_alerter_wnd_data(alerter_wnd_data * d) { + alerter_alert_data * adata; + + LDELETE(&khui_alert_windows, d); + + QGET(d, &adata); + while(adata) { + + if (adata->alert) { + + khui_alert_lock(adata->alert); + + adata->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW; + + adata->alert->displayed = FALSE; + + khui_alert_unlock(adata->alert); + + khui_alert_release(adata->alert); + adata->alert = NULL; + } + + PFREE(adata); + + QGET(d, &adata); + } + + PFREE(d); +} + +/* both ref and to_add must be locked and held */ +static khm_boolean +alert_can_consolidate(khui_alert * ref, + khui_alert * to_add, + alert_list * alist) { + + /* first check if we can add anything */ + if (alist->n_alerts == ARRAYLENGTH(alist->alerts)) + return FALSE; + +#ifdef DEBUG + assert(to_add != NULL); +#endif + + if (ref == NULL) { + /* we are testing whether to_add should be added to the alist + on its own. */ + if ((to_add->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) && + !(to_add->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW)) { + /* already displayed */ + return FALSE; + } + + if ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON | + KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON) { + /* needs to be shown in a balloon */ + return FALSE; + } + + return TRUE; + } + + /* if the ref or to_add are marked for modal, then we can't + consolidate them */ + if ((ref->flags & KHUI_ALERT_FLAG_MODAL) || + (to_add->flags & KHUI_ALERT_FLAG_MODAL)) + return FALSE; + + /* also, if either of them have requested to be exclusively shown + in a balloon, then we can't consolidate them. */ + if (((ref->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON | + KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON) + + || + + ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON | + KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON)) + return FALSE; + + /* for now, all we check if whether they are of the same type. */ + if (ref->alert_type != KHUI_ALERTTYPE_NONE && + ref->alert_type == to_add->alert_type) + return TRUE; + else + return FALSE; +} + +/* the return value is the number of alerts added to alist */ +static khm_int32 +alert_consolidate(alert_list * alist, + khui_alert * alert, + khm_boolean add_from_queue) { + + khui_alert * listtop; + int queue_size = 0; + int i; + khm_int32 n_added = 0; + +#ifdef DEBUG + assert(alist); +#endif + + if (alist->n_alerts == ARRAYLENGTH(alist->alerts)) { + /* can't add anything */ + + return 0; + } + + /* if the list is empty, we just add one alert */ + if (alist->n_alerts == 0) { + + if (alert) { + khui_alert_lock(alert); + if (alert_can_consolidate(NULL, alert, alist)) { + alert_list_add_alert(alist, alert); + n_added ++; + alert = NULL; + } + khui_alert_unlock(alert); + } + + if (n_added == 0 && add_from_queue) { + khui_alert * q; + int s, i; + + s = alert_queue_get_size(); + for (i=0; i < s && n_added == 0; i++) { + q = alert_queue_get_alert_by_pos(i); + if (q) { + khui_alert_lock(q); + if (alert_can_consolidate(NULL, q, alist)) { + alert_list_add_alert(alist, q); + n_added++; + alert_queue_delete_alert(q); + } + khui_alert_unlock(q); + khui_alert_release(q); + } + } + } + + if (n_added == 0) { + /* nothing to add */ + return 0; + } + } + + /* at this point, the alert list is not empty */ +#ifdef DEBUG + assert(alist->n_alerts != 0); + assert(alist->alerts[0]); +#endif + + listtop = alist->alerts[0]; + khui_alert_hold(listtop); + khui_alert_lock(listtop); + + queue_size = alert_queue_get_size(); + + if (alert) { + khui_alert_lock(alert); + if (alert_can_consolidate(listtop, alert, alist)) { + alert_list_add_alert(alist, alert); + n_added ++; + } + khui_alert_unlock(alert); + } + + if (add_from_queue) { + for (i=0; i < queue_size; i++) { + khui_alert * a; + + a = alert_queue_get_alert_by_pos(i); + if (a == NULL) + continue; + + khui_alert_lock(a); + if (alert_can_consolidate(listtop, a, alist)) { + alert_queue_delete_alert(a); + alert_list_add_alert(alist, a); + n_added ++; + queue_size = alert_queue_get_size(); + } + khui_alert_unlock(a); + khui_alert_release(a); + } + } + + khui_alert_unlock(listtop); + khui_alert_release(listtop); + + return n_added; +} + +static khm_int32 +alert_check_consolidate_window(alerter_wnd_data * d, khui_alert * a) { + alert_list alist; + alerter_alert_data * adata; + int n_added; + + alert_list_init(&alist); + + adata = QTOP(d); + while(adata) { + +#ifdef DEBUG + assert(adata->alert); +#endif + alert_list_add_alert(&alist, adata->alert); + + adata = QNEXT(adata); + } + + n_added = alert_consolidate(&alist, a, FALSE); + + alert_list_destroy(&alist); + + return n_added; +} + +static khm_int32 +alert_show_minimized(khui_alert * a) { + wchar_t tbuf[64]; /* corresponds to NOTIFYICONDATA::szInfoTitle[] */ + wchar_t mbuf[256]; /* corresponds to NOTIFYICONDATA::szInfo[] */ + +#ifdef DEBUG + assert(a); +#endif + if (a == NULL) + return KHM_ERROR_INVALID_PARAM; + + khui_alert_lock(a); + + if (a->message == NULL) + goto done; + + if (a->title == NULL) { + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + tbuf, ARRAYLENGTH(tbuf)); + } else { + StringCbCopy(tbuf, sizeof(tbuf), a->title); + } + + if (FAILED(StringCbCopy(mbuf, sizeof(mbuf), a->message)) || + (!(a->flags & KHUI_ALERT_FLAG_DEFACTION) && + (a->n_alert_commands > 0 || + a->suggestion || + (a->flags & KHUI_ALERT_FLAG_VALID_ERROR)))) { + /* if mbuf wasn't big enough, this should have copied a + truncated version of it */ + size_t cch_m, cch_p; + wchar_t postfix[256]; + + cch_p = LoadString(khm_hInstance, IDS_ALERT_MOREINFO, postfix, + ARRAYLENGTH(postfix)); + cch_p++; /* account for NULL */ + + StringCchLength(mbuf, ARRAYLENGTH(mbuf), &cch_m); + cch_m = min(cch_m, ARRAYLENGTH(mbuf) - cch_p); + + StringCchCopy(mbuf + cch_m, ARRAYLENGTH(mbuf) - cch_m, + postfix); + + a->flags |= KHUI_ALERT_FLAG_REQUEST_WINDOW; + } + + a->flags |= KHUI_ALERT_FLAG_DISPLAY_BALLOON; + +#if (_WIN32_IE >= 0x0501) + if (balloon_alert) { + khui_alert_release(balloon_alert); + balloon_alert = NULL; + } + + balloon_alert = a; + khui_alert_hold(a); +#endif + + khm_notify_icon_balloon(a->severity, + tbuf, + mbuf, + NTF_TIMEOUT); + + done: + khui_alert_unlock(a); + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +alert_show_normal(khui_alert * a) { + wchar_t buf[256]; + wchar_t * title; + alert_list alist; + + khui_alert_lock(a); + + if(a->title == NULL) { + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + buf, ARRAYLENGTH(buf)); + title = buf; + } else + title = a->title; + + khui_alert_unlock(a); + + alert_list_init(&alist); + alert_list_set_title(&alist, title); + alert_list_add_alert(&alist, a); + + alert_show_list(&alist); + + alert_list_destroy(&alist); + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +alert_show_list(alert_list * alist) { + HWND hwa; + + /* we don't need to keep track of the window handle because the window procedure adds it to the dialog list automatically */ - hwa = - CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP, - MAKEINTATOM(atom_alerter), - title, - WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN, - 0, 0, 300, 300, // bogus values - khm_hwnd_main, - (HMENU) NULL, - khm_hInstance, - (LPVOID) a); + hwa = + CreateWindowEx(ALERT_WINDOW_EX_SYLES, + MAKEINTATOM(atom_alerter), + alist->title, + ALERT_WINDOW_STYLES, + 0, 0, 300, 300, // bogus values + khm_hwnd_main, + (HMENU) NULL, + khm_hInstance, + (LPVOID) alist); + + ShowWindow(hwa, SW_SHOW); + + return (hwa != NULL); +} + +static khm_int32 +alert_show(khui_alert * a) { + khm_boolean show_normal = FALSE; + khm_boolean show_mini = FALSE; + + khui_alert_lock(a); + + /* is there an alert already? If so, we just enqueue the message + and let it sit. */ + if (ALERT_DISPLAYED() && + !(a->flags & KHUI_ALERT_FLAG_MODAL)) { + khm_int32 rv; + alerter_wnd_data * wdata; + + khui_alert_unlock(a); + + /* if there are any alerter windows displayed, check if this + alert can be consolidated with any of them. If so, we + should consolidate it. Otherwise, just enqueue it. */ + for(wdata = khui_alert_windows; + wdata; + wdata = LNEXT(wdata)) { + if (alert_check_consolidate_window(wdata, a)) { + + add_alert_to_wnd_data(wdata, a); + estimate_alerter_wnd_sizes(wdata); + setup_alerter_window_controls(wdata); + + return KHM_ERROR_SUCCESS; + + } + } + + rv = alert_enqueue(a); + + if (KHM_SUCCEEDED(rv)) + return KHM_ERROR_HELD; + else + return rv; + } + + if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) || + ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) && + !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW))) { + + /* The alert has already been displayed. */ + + show_normal = FALSE; + show_mini = FALSE; + + } else { + + if(a->err_context != NULL || + a->err_event != NULL) { + a->flags |= KHUI_ALERT_FLAG_VALID_ERROR; + } + + /* depending on the state of the main window, we + need to either show a window or a balloon */ + if ((a->flags & KHUI_ALERT_FLAG_MODAL) || + (khm_is_main_window_active() && + !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)) || + (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW)) { - ShowWindow(hwa, SW_SHOW); + show_normal = TRUE; - return KHM_ERROR_SUCCESS; -} + } else { -static khm_int32 -alert_show(khui_alert * a) { - /* is there an alert already? If so, we just enqueue the message - and let it sit. */ - if (current_alert) { - return alert_enqueue(a); + show_mini = TRUE; + + } } - /* the window has already been shown */ - if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) || - ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) && - !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW))) + khui_alert_unlock(a); + + if (show_normal) + return alert_show_normal(a); + else if (show_mini) + return alert_show_minimized(a); + else return KHM_ERROR_SUCCESS; +} + +static void +check_for_queued_alerts(void) { + if (!is_alert_queue_empty()) { + khui_alert * a; + + a = alert_queue_peek(); - if(a->err_context != NULL || - a->err_event != NULL) { khui_alert_lock(a); - a->flags |= KHUI_ALERT_FLAG_VALID_ERROR; + + if (a->title) { + HICON hi; + int res; + + if (a->severity == KHERR_ERROR) + res = OIC_ERROR; + else if (a->severity == KHERR_WARNING) + res = OIC_WARNING; + else + res = OIC_INFORMATION; + + hi = LoadImage(0, MAKEINTRESOURCE(res), + IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), + LR_SHARED); + + khm_statusbar_set_part(KHUI_SBPART_NOTICE, + hi, + a->title); + } + khui_alert_unlock(a); - } + khui_alert_release(a); - /* depending on the state of the main window, we - need to either show a window or a balloon */ - if ((a->flags & KHUI_ALERT_FLAG_MODAL) || - (khm_is_main_window_active() && - !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON))) - return alert_show_normal(a); - else - return alert_show_minimized(a); + } else { + khm_statusbar_set_part(KHUI_SBPART_NOTICE, + NULL, NULL); + } } static khm_int32 @@ -554,7 +1700,7 @@ alert_enqueue(khui_alert * a) { if (is_alert_queue_full()) return KHM_ERROR_NO_RESOURCES; - add_to_alert_queue(a); + alert_queue_put_alert(a); check_for_queued_alerts(); return KHM_ERROR_SUCCESS; @@ -570,29 +1716,14 @@ alerter_wnd_proc(HWND hwnd, switch(uMsg) { case WM_CREATE: { - LONG dlgb; - HWND hwnd_parent; - RECT r_parent; - POINT pos; - SIZE s; LPCREATESTRUCT lpcs; - khui_alert * a; + alert_list * alist; alerter_wnd_data * d; lpcs = (LPCREATESTRUCT) lParam; - a = (khui_alert *) lpcs->lpCreateParams; - khui_alert_hold(a); - - d = PMALLOC(sizeof(*d)); - ZeroMemory(d, sizeof(*d)); - - d->alert = a; - d->hwnd = hwnd; + alist = (alert_list *) lpcs->lpCreateParams; - khui_alert_lock(a); - - a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW; - LPUSH(&khui_alerts, d); + d = create_alerter_wnd_data(hwnd, alist); #pragma warning(push) #pragma warning(disable: 4244) @@ -602,444 +1733,282 @@ alerter_wnd_proc(HWND hwnd, khm_add_dialog(hwnd); khm_enter_modal(hwnd); - /* now figure out the size and position of the window */ - - hwnd_parent = GetWindow(hwnd, GW_OWNER); - GetWindowRect(hwnd_parent, &r_parent); - - dlgb = GetDialogBaseUnits(); + estimate_alerter_wnd_sizes(d); + setup_alerter_window_controls(d); -#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4) -#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8) - - d->dx_margin = DLG2SCNX(NTF_MARGIN); - d->dy_margin = DLG2SCNY(NTF_MARGIN); - - d->x_message = DLG2SCNX(NTF_MSG_X); - d->dx_message = DLG2SCNX(NTF_MSG_WIDTH); - - if (a->message) { - d->dy_message = DLG2SCNY(NTF_MSG_HEIGHT); - } - - if (a->suggestion) { - d->dy_suggestion = DLG2SCNY(NTF_SUG_HEIGHT); - d->dx_suggest_pad = DLG2SCNX(NTF_SUG_PAD); + if (d->hw_close) { + SetFocus(d->hw_close); } - d->dy_bb = DLG2SCNY(NTF_BB_HEIGHT); - d->dx_button = DLG2SCNX(NTF_BUTTON_WIDTH); - d->dy_button = DLG2SCNY(NTF_BUTTON_HEIGHT); - d->dx_button_incr = DLG2SCNX(NTF_BUTTON_XINCR); - - d->dx_icon = DLG2SCNX(NTF_ICON_WIDTH); - d->dy_icon = DLG2SCNY(NTF_ICON_HEIGHT); + return TRUE; + } + break; /* not reached */ - d->dx_client = DLG2SCNX(NTF_WIDTH); - d->dy_client = max(d->dy_icon, - d->dy_message + - ((d->dy_suggestion > 0)? - (d->dy_suggestion + d->dy_margin): - 0)) + - d->dy_margin * 3 + d->dy_bb; + case WM_DESTROY: + { + alerter_wnd_data * d; - /* adjust for client rect */ - s.cx = d->dx_client; - s.cy = d->dy_client; + /* khm_leave_modal() could be here, but instead it is in + the WM_COMMAND handler. This is because the modal loop + has to be exited before DestroyWindow() is issued. */ + //khm_leave_modal(); + khm_del_dialog(hwnd); - { - RECT c_r; - RECT w_r; + d = (alerter_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, NTF_PARAM); - GetWindowRect(hwnd, &w_r); - GetClientRect(hwnd, &c_r); + destroy_alerter_wnd_data(d); - s.cx += (w_r.right - w_r.left) - (c_r.right - c_r.left); - s.cy += (w_r.bottom - w_r.top) - (c_r.bottom - c_r.top); - } + return TRUE; + } + break; - pos.x = (r_parent.left + r_parent.right - s.cx) / 2; - pos.y = (r_parent.top + r_parent.bottom - s.cy) / 2; +#if 0 + case WM_PAINT: + { + RECT r_update; + PAINTSTRUCT ps; + HDC hdc; + alerter_wnd_data * d; - SetWindowPos(hwnd, - HWND_TOP, - pos.x, pos.y, - s.cx, s.cy, - SWP_SHOWWINDOW); + if(!GetUpdateRect(hwnd, &r_update, TRUE)) + return FALSE; - { - LOGFONT lf; - HDC hdc_dt; - - hdc_dt = GetDC(NULL); - - lf.lfHeight = -MulDiv(8, - GetDeviceCaps(hdc_dt, LOGPIXELSY), - 72); - lf.lfWidth = 0; - lf.lfEscapement = 0; - lf.lfOrientation = 0; - lf.lfWeight = FW_NORMAL; - lf.lfItalic = FALSE; - lf.lfUnderline = FALSE; - lf.lfStrikeOut = FALSE; - lf.lfCharSet = DEFAULT_CHARSET; - lf.lfOutPrecision = OUT_DEFAULT_PRECIS; - lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; - lf.lfQuality = DEFAULT_QUALITY; - lf.lfPitchAndFamily = DEFAULT_PITCH; - - LoadString(khm_hInstance, IDS_DEFAULT_FONT, - lf.lfFaceName, ARRAYLENGTH(lf.lfFaceName)); - - d->hfont = CreateFontIndirect(&lf); - - ReleaseDC(NULL, hdc_dt); - } + d = (alerter_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, NTF_PARAM); - /* create dialog controls now */ - { - int x,y; - int width, height; - int i; + hdc = BeginPaint(hwnd, &ps); - x = d->x_message; - y = d->dy_client - d->dy_bb; - width = d->dx_button; - height = d->dy_button; + EndPaint(hwnd, &ps); - for(i=0; in_alert_commands; i++) { - wchar_t caption[256]; - khui_action * action; - HWND hw_button; + return FALSE; + } + break; /* not reached */ +#endif - if(a->alert_commands[i] == 0) - continue; + case WM_COMMAND: + { + alerter_wnd_data * d; - action = khui_find_action(a->alert_commands[i]); - if(action == NULL) - continue; + d = (alerter_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, NTF_PARAM); - LoadString(khm_hInstance, action->is_caption, - caption, ARRAYLENGTH(caption)); - - hw_button = - CreateWindowEx(0, - L"BUTTON", - caption, - WS_VISIBLE | WS_CHILD | - /* the first button is the default */ - ((i==0)? BS_DEFPUSHBUTTON: 0), - x,y,width,height, - hwnd, - (HMENU)(INT_PTR) (action->cmd), - khm_hInstance, - NULL); + if(HIWORD(wParam) == BN_CLICKED) { + if (LOWORD(wParam) == IDC_NTF_CLOSE || + LOWORD(wParam) == KHUI_PACTION_NEXT) { - SendMessage(hw_button, WM_SETFONT, - (WPARAM) d->hfont, MAKELPARAM(TRUE, 0)); + khm_leave_modal(); - d->hwnd_buttons[i] = hw_button; + DestroyWindow(hwnd); - x += d->dx_button_incr; +#ifdef FORLATER + if (LOWORD(wParam) == KHUI_PACTION_NEXT) { + kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0); + } +#endif + return 0; } } + } + break; + } - if (d->hwnd_buttons[0]) - SetFocus(d->hwnd_buttons[0]); + return DefDlgProc(hwnd, uMsg, wParam, lParam); + //return DefWindowProc(hwnd, uMsg, wParam, lParam); +} - khm_notify_icon_change(a->severity); +static LRESULT CALLBACK +alert_bin_wnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + BOOL in_printclient = FALSE; - khui_alert_unlock(a); + switch(uMsg) { + case WM_CREATE: + { + LPCREATESTRUCT lpcs; + alerter_wnd_data * d; - d->metrics_done = FALSE; - - return TRUE; + lpcs = (LPCREATESTRUCT) lParam; + d = (alerter_wnd_data *) lpcs->lpCreateParams; + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) d); +#pragma warning(pop) } - break; /* not reached */ + return 0; - case WM_DESTROY: + case WM_PRINTCLIENT: + in_printclient = TRUE; + /* fallthrough */ + case WM_PAINT: { + HDC hdc; + PAINTSTRUCT ps; + RECT r; + HFONT hf_old; + int y; alerter_wnd_data * d; + alerter_alert_data * adata; + size_t len; - /* khm_leave_modal() could be here, but instead it is in - the WM_COMMAND handler. This is because the modal loop - has to be exited before DestroyWindow() is issued. */ - //khm_leave_modal(); - khm_del_dialog(hwnd); + d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA); +#ifdef DEBUG + assert(d); +#endif - d = (alerter_wnd_data *)(LONG_PTR) - GetWindowLongPtr(hwnd, NTF_PARAM); + if (in_printclient) { + hdc = (HDC) wParam; + } else { + hdc = BeginPaint(hwnd, &ps); + } - LDELETE(&khui_alerts, d); +#ifdef DEBUG + assert(hdc); + assert(d->hfont); +#endif - khui_alert_lock(d->alert); - d->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW; - if (d->alert->flags & KHUI_ALERT_FLAG_MODAL) - notifier_modal_loop = FALSE; - khui_alert_unlock(d->alert); + if (in_printclient || ps.fErase) { + HBRUSH hb_background; - khui_alert_release(d->alert); + hb_background = GetSysColorBrush(COLOR_BTNFACE); - DeleteObject(d->hfont); + GetClientRect(hwnd, &r); + FillRect(hdc, &r, hb_background); + } - PFREE(d); + SetBkMode(hdc, TRANSPARENT); - khm_notify_icon_change(KHERR_NONE); + hf_old = SelectFont(hdc, d->hfont); - return TRUE; - } - break; + y = -d->scroll_top; - case WM_PAINT: - { - RECT r_update; - PAINTSTRUCT ps; - HDC hdc; - LONG dlgb; - alerter_wnd_data * d; - HFONT hf_old; - BOOL need_resize = FALSE; + /* go through the alerts and display them */ + adata = QTOP(d); + while(adata) { + khui_alert * a; - if(!GetUpdateRect(hwnd, &r_update, TRUE)) - return FALSE; + a = adata->alert; +#ifdef DEBUG + assert(a != NULL); +#endif + khui_alert_lock(a); - d = (alerter_wnd_data *)(LONG_PTR) - GetWindowLongPtr(hwnd, NTF_PARAM); + /* if the alert has a title and it's different from + the original caption for the alert dialog, we have + to display the title. */ + if (a->title && + wcscmp(a->title, d->caption)) { - dlgb = GetDialogBaseUnits(); + CopyRect(&r, &adata->r_title); + OffsetRect(&r, 0, y); - hdc = BeginPaint(hwnd, &ps); + StringCchLength(a->title, KHUI_MAXCCH_TITLE, &len); - hf_old = SelectFont(hdc, d->hfont); + DrawEdge(hdc, &r, EDGE_RAISED, BF_RECT | BF_MIDDLE); - khui_alert_lock(d->alert); + InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy); - // draw the severity icon - { - HICON hicon; - int x,y; - int iid; - - /* GOINGHERE! If the metrics for the window haven't - been calculated yet, then calculate them. If the - hight needs to be expanded, then do that and wait - for the next repaint cycle. Also move the button - controls down. */ - x = d->dx_margin; - y = d->dy_margin; - - if(d->alert->severity == KHERR_ERROR) - iid = OIC_HAND; - else if(d->alert->severity == KHERR_WARNING) - iid = OIC_BANG; - else - iid = OIC_NOTE; + DrawText(hdc, a->title, (int) len, &r, + DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); + } - hicon = LoadImage(NULL, - MAKEINTRESOURCE(iid), - IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - LR_SHARED); + { + HICON hicon; + int iid; + + CopyRect(&r, &adata->r_icon); + OffsetRect(&r, 0, y); + + if(a->severity == KHERR_ERROR) + iid = OIC_HAND; + else if(a->severity == KHERR_WARNING) + iid = OIC_BANG; + else + iid = OIC_NOTE; + + hicon = (HICON) LoadImage(NULL, + MAKEINTRESOURCE(iid), + IMAGE_ICON, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), + LR_SHARED); + + DrawIcon(hdc, r.left, r.top, hicon); + } - DrawIcon(hdc, x, y, hicon); - } + if (a->message) { - // draw the message - if(d->alert->message) { - RECT r; - int width; - int height; - size_t cch; - - r.left = d->x_message; - r.top = d->dy_margin; - width = d->dx_message; - r.right = r.left + width; - height = d->dy_message; - r.bottom = r.top + height; - - StringCchLength(d->alert->message, - KHUI_MAXCCH_MESSAGE, &cch); - - height = DrawText(hdc, - d->alert->message, - (int) cch, - &r, - DT_WORDBREAK | - DT_CALCRECT); - - if (height > d->dy_message) { - d->dy_message = height; - need_resize = TRUE; - } else { - DrawText(hdc, - d->alert->message, - (int) cch, - &r, - DT_WORDBREAK); - } + CopyRect(&r, &adata->r_message); + OffsetRect(&r, 0, y); - d->y_message = r.top; - } + StringCchLength(a->message, KHUI_MAXCCH_MESSAGE, &len); - // and the suggestion - if (d->alert->suggestion) { - RECT r, ro; - int height; - size_t cch; - HICON h_sug_ico; - - r.left = d->x_message; - r.top = d->y_message + d->dy_message + d->dy_margin; - r.right = r.left + d->dx_message; - r.bottom = r.top + d->dy_suggestion; - - CopyRect(&ro, &r); - - // adjust for icon and padding - r.left += GetSystemMetrics(SM_CXSMICON) + d->dx_suggest_pad * 2; - r.top += d->dx_suggest_pad; - r.right -= d->dx_suggest_pad; - r.bottom -= d->dx_suggest_pad; - - StringCchLength(d->alert->suggestion, - KHUI_MAXCCH_SUGGESTION, &cch); - - height = DrawText(hdc, - d->alert->suggestion, - (int) cch, - &r, - DT_WORDBREAK | - DT_CALCRECT); - - if (height > d->dy_suggestion) { - d->dy_suggestion = height; - need_resize = TRUE; - } else { - int old_bk_mode; - - ro.bottom = r.bottom + d->dx_suggest_pad; - - FillRect(hdc, &ro, (HBRUSH) (COLOR_INFOBK + 1)); - DrawEdge(hdc, &ro, EDGE_SUNKEN, BF_FLAT | BF_RECT); - - h_sug_ico = - LoadImage(0, - MAKEINTRESOURCE(OIC_INFORMATION), - IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - LR_SHARED); - - assert(h_sug_ico != NULL); - - DrawIconEx(hdc, - ro.left + d->dx_suggest_pad, - ro.top + d->dx_suggest_pad, - h_sug_ico, - GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - 0, NULL, - DI_NORMAL); - - old_bk_mode = SetBkMode(hdc, TRANSPARENT); - - DrawText(hdc, - d->alert->suggestion, - (int) cch, - &r, + DrawText(hdc, a->message, (int) len, &r, DT_WORDBREAK); - - SetBkMode(hdc, old_bk_mode); } - d->y_suggestion = r.top; - } + if (a->suggestion) { + HICON hicon; - khui_alert_unlock(d->alert); + CopyRect(&r, &adata->r_suggestion); + OffsetRect(&r, 0, y); - SelectObject(hdc, hf_old); + DrawEdge(hdc, &r, EDGE_SUNKEN, BF_RECT | BF_MIDDLE); - EndPaint(hwnd, &ps); + InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy); - if (need_resize) { - RECT r; - int x,y; - int width, height; - int i; - - GetClientRect(hwnd, &r); + hicon = (HICON) LoadImage(NULL, + MAKEINTRESOURCE(OIC_NOTE), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_SHARED); - height = max(d->dy_icon, - d->dy_message + - ((d->dy_suggestion > 0)? - (d->dy_suggestion + d->dy_margin): - 0)) + - d->dy_margin * 3 + d->dy_bb; - r.bottom = r.top + height; - - d->dy_client = height; - - AdjustWindowRectEx(&r, - GetWindowLongPtr(hwnd, GWL_STYLE), - FALSE, - GetWindowLongPtr(hwnd, GWL_EXSTYLE)); - - SetWindowPos(hwnd, - NULL, - 0, 0, - r.right - r.left, - r.bottom - r.top, - SWP_NOACTIVATE | SWP_NOCOPYBITS | - SWP_NOMOVE | SWP_NOOWNERZORDER | - SWP_NOZORDER); - - InvalidateRect(hwnd, NULL, TRUE); - - x = d->x_message; - y = d->dy_client - d->dy_bb; - width = d->dx_button; - height = d->dy_button; - - for(i=0; ialert->n_alert_commands; i++) { - MoveWindow(d->hwnd_buttons[i], - x,y, - width,height, - TRUE); - - x += d->dx_button_incr; - } - } + DrawIcon(hdc, r.left, r.top, hicon); - return FALSE; - } - break; /* not reached */ + r.left += d->s_pad.cx + GetSystemMetrics(SM_CXSMICON); - case WM_COMMAND: - { - alerter_wnd_data * d; + StringCchLength(a->suggestion, KHUI_MAXCCH_SUGGESTION, &len); - d = (alerter_wnd_data *)(LONG_PTR) - GetWindowLongPtr(hwnd, NTF_PARAM); + DrawText(hdc, a->suggestion, (int) len, &r, + DT_WORDBREAK); + } + khui_alert_unlock(a); - if(HIWORD(wParam) == BN_CLICKED) { - khui_alert_lock(d->alert); - d->alert->response = LOWORD(wParam); - khui_alert_unlock(d->alert); + y += adata->r_alert.bottom; - khm_leave_modal(); + adata = QNEXT(adata); + } - DestroyWindow(hwnd); + SelectFont(hdc, hf_old); - if (LOWORD(wParam) == KHUI_PACTION_NEXT) - kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0); - return 0; + if (!in_printclient) { + EndPaint(hwnd, &ps); } } + return 0; + + case WM_VSCROLL: + { + } + return 0; + + case WM_COMMAND: + { + } break; + + case WM_DESTROY: + { + } + return 0; } - return DefDlgProc(hwnd, uMsg, wParam, lParam); - //return DefWindowProc(hwnd, uMsg, wParam, lParam); + return DefWindowProc(hwnd, uMsg, wParam, lParam); } ATOM khm_register_alerter_wnd_class(void) @@ -1061,7 +2030,7 @@ ATOM khm_register_alerter_wnd_class(void) wcx.hInstance = khm_hInstance; wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP)); wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); - wcx.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wcx.lpszMenuName = NULL; wcx.lpszClassName = KHUI_ALERTER_CLASS; wcx.hIconSm = NULL; @@ -1071,6 +2040,31 @@ ATOM khm_register_alerter_wnd_class(void) return atom_alerter; } +ATOM khm_register_alert_bin_wnd_class(void) +{ + WNDCLASSEX wcx; + + ZeroMemory(&wcx, sizeof(wcx)); + + wcx.cbSize = sizeof(wcx); + wcx.style = CS_OWNDC; + + wcx.lpfnWndProc = alert_bin_wnd_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = sizeof(LONG_PTR); + wcx.hInstance = khm_hInstance; + wcx.hIcon = NULL; + wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); + wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_ALERTBIN_CLASS; + wcx.hIconSm = NULL; + + atom_alert_bin = RegisterClassEx(&wcx); + + return atom_alert_bin; +} + /********************************************************************** Notification Icon ***********************************************************************/ @@ -1140,19 +2134,20 @@ khm_notify_icon_balloon(khm_int32 severity, /* too long? */ StringCchCopyN(ni.szInfo, ARRAYLENGTH(ni.szInfo), msg, - ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELIPSIS)); + ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELLIPSIS)); StringCchCat(ni.szInfo, ARRAYLENGTH(ni.szInfo), - ELIPSIS); + ELLIPSIS); } if (FAILED(StringCbCopy(ni.szInfoTitle, sizeof(ni.szInfoTitle), title))) { StringCchCopyN(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle), title, - ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELIPSIS)); + ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELLIPSIS)); StringCchCat(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle), - ELIPSIS); + ELLIPSIS); } + ni.uTimeout = timeout; Shell_NotifyIcon(NIM_MODIFY, &ni); @@ -1177,7 +2172,7 @@ void khm_notify_icon_expstate(enum khm_notif_expstate expseverity) { iid_normal = new_iid; - if (current_alert == NULL) + if (balloon_alert == NULL) khm_notify_icon_change(KHERR_NONE); } @@ -1239,6 +2234,9 @@ void khm_init_notifier(void) if(!khm_register_alerter_wnd_class()) return; + if(!khm_register_alert_bin_wnd_class()) + return; + hwnd_notifier = CreateWindowEx(0, MAKEINTATOM(atom_notifier), KHUI_NOTIFIER_WINDOW, @@ -1255,12 +2253,11 @@ void khm_init_notifier(void) notifier_ready = TRUE; khm_notify_icon_add(); - } + } else { #ifdef DEBUG - else { assert(hwnd_notifier != NULL); - } #endif + } khm_timer_init(); khm_addr_change_notifier_init(); @@ -1290,5 +2287,10 @@ void khm_exit_notifier(void) atom_alerter = 0; } + if(atom_alert_bin != 0) { + UnregisterClass(MAKEINTATOM(atom_alert_bin), khm_hInstance); + atom_alert_bin = 0; + } + notifier_ready = FALSE; } diff --git a/src/windows/identity/ui/propertywnd.c b/src/windows/identity/ui/propertywnd.c index 79a6cc35a..4061cd78d 100644 --- a/src/windows/identity/ui/propertywnd.c +++ b/src/windows/identity/ui/propertywnd.c @@ -230,7 +230,7 @@ khm_int32 khm_register_propertywnd_class(void) wcx.hInstance = khm_hInstance; wcx.hIcon = NULL; wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); - wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); + wcx.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); wcx.lpszMenuName = NULL; wcx.lpszClassName = KHUI_PROPERTYWND_CLASS_NAME; wcx.hIconSm = NULL; diff --git a/src/windows/identity/ui/reqdaemon.c b/src/windows/identity/ui/reqdaemon.c index b95f02daf..e72e3e22b 100644 --- a/src/windows/identity/ui/reqdaemon.c +++ b/src/windows/identity/ui/reqdaemon.c @@ -350,6 +350,8 @@ khm_reqdaemon_thread_proc(LPVOID vparam) { DWORD dw; #endif + PDESCTHREAD(L"Remote Request Daemon", L"App"); + khm_register_reqdaemonwnd_class(); #ifdef DEBUG diff --git a/src/windows/identity/ui/resource.h b/src/windows/identity/ui/resource.h index f430fe0b0..69d35b316 100644 --- a/src/windows/identity/ui/resource.h +++ b/src/windows/identity/ui/resource.h @@ -121,7 +121,6 @@ #define IDD_NC_NEWCRED 162 #define IDD_NC_BBAR 163 #define IDS_ALERT_NOSEL_TITLE 163 -#define IDD_NC_TS 164 #define IDS_ALERT_NOSEL 164 #define IDI_ENABLED 165 #define IDS_NC_CREDTEXT_ID_VALID 165 @@ -301,6 +300,11 @@ #define IDS_IDACTION_RENEW 294 #define IDS_IDACTION_DESTROY 295 #define IDS_CTX_DESTROY_ID 296 +#define IDS_NCN_IDENT_INVALID 297 +#define IDS_NCN_IDENT_CHECKING 298 +#define IDS_NCN_IDENT_UNKNOWN 299 +#define IDS_REMOTE_FAIL 300 +#define IDS_REMOTE_FAIL_TITLE 301 #define IDC_NC_USERNAME 1007 #define IDC_NC_PASSWORD 1008 #define IDC_NC_CREDTEXT_LABEL 1009 @@ -309,6 +313,7 @@ #define IDC_NC_CREDTEXT 1012 #define IDC_NC_HELP 1017 #define IDC_NC_OPTIONS 1019 +#define IDC_NC_ADVANCED 1019 #define IDC_PP_IDNAME 1026 #define IDC_PP_IDDEF 1027 #define IDC_PP_IDSEARCH 1028 diff --git a/src/windows/identity/ui/uiconfig.csv b/src/windows/identity/ui/uiconfig.csv index 9f5e5e4b3..1cde80055 100644 --- a/src/windows/identity/ui/uiconfig.csv +++ b/src/windows/identity/ui/uiconfig.csv @@ -37,6 +37,9 @@ CredWindow,KC_SPACE,0,Options for the credentials window Main,KC_ENDSPACE,0, NewCred,KC_SPACE,0,New credentials window ForceToTop,KC_INT32,1,Force new creds window to the top + AnimateSizeChanges,KC_INT32,1,Animate the new creds window when the size needs changing. + AnimationSteps,KC_INT32,7,Number of steps in size-change animation + AnimationStepTimeout,KC_INT32,40,Number of milliseconds to wait between each step of the size-change animation NewCred,KC_ENDSPACE,0, Windows,KC_ENDSPACE,0, Views,KC_SPACE,0,Preconfigured views for credentials diff --git a/src/windows/identity/uilib/Makefile b/src/windows/identity/uilib/Makefile index 6a9fb53ba..5686db523 100644 --- a/src/windows/identity/uilib/Makefile +++ b/src/windows/identity/uilib/Makefile @@ -52,7 +52,8 @@ INCFILES= \ $(INCDIR)\khconfigui.h \ $(INCDIR)\khtracker.h \ $(INCDIR)\khremote.h \ - $(INCDIR)\intaction.h + $(INCDIR)\intaction.h \ + $(INCDIR)\intalert.h $(OBJ)\actiondef.c: actions.csv actiondef.cfg $(CCSV) $** $@ diff --git a/src/windows/identity/uilib/action.c b/src/windows/identity/uilib/action.c index e38726879..7c42889de 100644 --- a/src/windows/identity/uilib/action.c +++ b/src/windows/identity/uilib/action.c @@ -261,6 +261,21 @@ khui_exit_actions(void) { DeleteCriticalSection(&cs_actions); } +KHMEXP void KHMAPI +khui_refresh_actions(void) { + kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0); +} + +KHMEXP void KHMAPI +khui_action_lock(void) { + EnterCriticalSection(&cs_actions); +} + +KHMEXP void KHMAPI +khui_action_unlock(void) { + LeaveCriticalSection(&cs_actions); +} + KHMEXP khm_int32 KHMAPI khui_action_create(const wchar_t * name, const wchar_t * caption, @@ -350,29 +365,36 @@ khui_action_create(const wchar_t * name, KHMEXP void * KHMAPI khui_action_get_data(khm_int32 action) { khui_action * act; + void * data; + EnterCriticalSection(&cs_actions); act = khui_find_action(action); - - if (act == NULL) - return NULL; + if (act == NULL || (act->state & KHUI_ACTIONSTATE_DELETED)) + data = NULL; else - return act->data; + data = act->data; + LeaveCriticalSection(&cs_actions); + + return data; } KHMEXP void KHMAPI khui_action_delete(khm_int32 action) { khui_action * act; + EnterCriticalSection(&cs_actions); + act = khui_find_action(action); - if (act == NULL) + if (act == NULL) { + LeaveCriticalSection(&cs_actions); return; + } /* for the moment, even when the action is deleted, we don't free up the block of memory used by the khui_action structure. When a new action is created, it will reuse deleted action structures. */ - EnterCriticalSection(&cs_actions); act->state |= KHUI_ACTIONSTATE_DELETED; if (act->name) PFREE(act->name); @@ -470,6 +492,8 @@ khui_menu_dup(khui_menu_def * src) size_t i; size_t n; + EnterCriticalSection(&cs_actions); + d = khui_menu_create(src->cmd); if (!(src->state & KHUI_MENUSTATE_ALLOCD)) @@ -485,6 +509,8 @@ khui_menu_dup(khui_menu_def * src) } } + LeaveCriticalSection(&cs_actions); + return d; } @@ -504,13 +530,13 @@ khui_menu_delete(khui_menu_def * d) } EnterCriticalSection(&cs_actions); + for (i=0; i < khui_n_cust_menus; i++) { if (khui_cust_menus[i] == d) { khui_cust_menus[i] = NULL; break; } } - LeaveCriticalSection(&cs_actions); for(i=0; i< (int) d->n_items; i++) { if(d->items[i].flags & KHUI_ACTIONREF_FREE_PACTION) @@ -520,6 +546,8 @@ khui_menu_delete(khui_menu_def * d) if(d->items) PFREE(d->items); PFREE(d); + + LeaveCriticalSection(&cs_actions); } static void @@ -563,6 +591,9 @@ menu_const_to_allocd(khui_menu_def * d) KHMEXP void KHMAPI khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_int32 flags) { + + EnterCriticalSection(&cs_actions); + if (!(d->state & KHUI_MENUSTATE_ALLOCD)) menu_const_to_allocd(d); @@ -584,6 +615,8 @@ khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_i d->items[idx].flags |= KHUI_ACTIONREF_SEP; d->n_items++; + + LeaveCriticalSection(&cs_actions); } KHMEXP void KHMAPI @@ -593,6 +626,8 @@ khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction, if (paction == NULL) return; + EnterCriticalSection(&cs_actions); + if (!(d->state & KHUI_MENUSTATE_ALLOCD)) menu_const_to_allocd(d); @@ -611,50 +646,72 @@ khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction, d->items[idx].p_action = paction; d->n_items++; + + LeaveCriticalSection(&cs_actions); } KHMEXP void KHMAPI khui_menu_remove_action(khui_menu_def * d, khm_size idx) { + EnterCriticalSection(&cs_actions); + if (!(d->state & KHUI_MENUSTATE_ALLOCD)) menu_const_to_allocd(d); assert(d->state & KHUI_MENUSTATE_ALLOCD); - if (idx < 0 || idx >= d->n_items) - return; + if (idx >= 0 && idx < d->n_items) { + + if (idx < d->n_items - 1) { + memmove(&d->items[idx], &d->items[idx + 1], + ((d->n_items - 1) - idx) * sizeof(d->items[0])); + } + + d->n_items--; - if (idx < d->n_items - 1) { - memmove(&d->items[idx], &d->items[idx + 1], - ((d->n_items - 1) - idx) * sizeof(d->items[0])); } - d->n_items--; + LeaveCriticalSection(&cs_actions); } KHMEXP khm_size KHMAPI khui_menu_get_size(khui_menu_def * d) { + khm_size size; + + EnterCriticalSection(&cs_actions); + if (d->state & KHUI_MENUSTATE_ALLOCD) - return d->n_items; + size = d->n_items; else - return khui_action_list_length(d->items); + size = khui_action_list_length(d->items); + + LeaveCriticalSection(&cs_actions); + + return size; } KHMEXP khui_action_ref * khui_menu_get_action(khui_menu_def * d, khm_size idx) { + khui_action_ref * act = NULL; khm_size n; + EnterCriticalSection(&cs_actions); + if (d->state & KHUI_MENUSTATE_ALLOCD) n = d->n_items; else n = khui_action_list_length(d->items); if (idx < 0 || idx >= n) - return NULL; + act = NULL; + else + act = &d->items[idx]; + + LeaveCriticalSection(&cs_actions); - return &d->items[idx]; + return act; } KHMEXP khui_menu_def * KHMAPI @@ -663,6 +720,9 @@ khui_find_menu(khm_int32 id) { int i; if (id < KHUI_USERACTION_BASE) { + + /* the list of system menus are considered immutable. */ + d = khui_all_menus; for(i=0;icmd == id); #endif - if (act && act->state & KHUI_ACTIONSTATE_DELETED) + if (act && (act->state & KHUI_ACTIONSTATE_DELETED)) act = NULL; } LeaveCriticalSection(&cs_actions); @@ -732,30 +792,43 @@ khui_find_named_action(const wchar_t * name) { return &act[i]; } + act = NULL; + + EnterCriticalSection(&cs_actions); + pact = khui_cust_actions; for(i=0;iname) continue; if(!wcscmp(pact[i]->name, name)) { - if (pact[i]->state & KHUI_ACTIONSTATE_DELETED) - return NULL; - else - return pact[i]; + + if (!(pact[i]->state & KHUI_ACTIONSTATE_DELETED)) { + act = pact[i]; + } + break; } } - return NULL; + LeaveCriticalSection(&cs_actions); + + return act; } KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref) { size_t c = 0; + + EnterCriticalSection(&cs_actions); + while(ref && ref->action != KHUI_MENU_END && !(ref->flags & KHUI_ACTIONREF_END)) { c++; ref++; } + + LeaveCriticalSection(&cs_actions); + return c; } @@ -765,6 +838,8 @@ khui_check_radio_action(khui_menu_def * d, khm_int32 cmd) khui_action_ref * r; khui_action * act; + EnterCriticalSection(&cs_actions); + r = d->items; while(r && r->action != KHUI_MENU_END && (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) { @@ -783,6 +858,8 @@ khui_check_radio_action(khui_menu_def * d, khm_int32 cmd) r++; } + LeaveCriticalSection(&cs_actions); + kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0); } @@ -794,12 +871,18 @@ khui_check_action(khm_int32 cmd, khm_boolean check) { if (!act) return; + EnterCriticalSection(&cs_actions); + if (check && !(act->state & KHUI_ACTIONSTATE_CHECKED)) act->state |= KHUI_ACTIONSTATE_CHECKED; else if (!check && (act->state & KHUI_ACTIONSTATE_CHECKED)) act->state &= ~KHUI_ACTIONSTATE_CHECKED; - else + else { + LeaveCriticalSection(&cs_actions); return; + } + + LeaveCriticalSection(&cs_actions); kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0); } @@ -811,6 +894,8 @@ khui_enable_actions(khui_menu_def * d, khm_boolean enable) int delta = FALSE; khui_action * act; + EnterCriticalSection(&cs_actions); + r = d->items; while(r && r->action != KHUI_MENU_END && (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) { @@ -834,6 +919,8 @@ khui_enable_actions(khui_menu_def * d, khm_boolean enable) r++; } + LeaveCriticalSection(&cs_actions); + if(delta) { kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0); } @@ -847,12 +934,18 @@ khui_enable_action(khm_int32 cmd, khm_boolean enable) { if (!act) return; + EnterCriticalSection(&cs_actions); + if (enable && (act->state & KHUI_ACTIONSTATE_DISABLED)) { act->state &= ~KHUI_ACTIONSTATE_DISABLED; } else if (!enable && !(act->state & KHUI_ACTIONSTATE_DISABLED)) { act->state |= KHUI_ACTIONSTATE_DISABLED; - } else + } else { + LeaveCriticalSection(&cs_actions); return; + } + + LeaveCriticalSection(&cs_actions); kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0); } diff --git a/src/windows/identity/uilib/actiondef.cfg b/src/windows/identity/uilib/actiondef.cfg index 0627a67e5..561335257 100644 --- a/src/windows/identity/uilib/actiondef.cfg +++ b/src/windows/identity/uilib/actiondef.cfg @@ -33,6 +33,7 @@ Do not modify directly. #include #include +#include #include"../ui/resource.h" khui_action khui_actions [] = { diff --git a/src/windows/identity/uilib/alert.c b/src/windows/identity/uilib/alert.c index 96436543a..e398690e9 100644 --- a/src/windows/identity/uilib/alert.c +++ b/src/windows/identity/uilib/alert.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -62,7 +63,11 @@ khui_alert_create_empty(khui_alert ** result) /* set defaults */ a->severity = KHERR_INFO; a->flags = KHUI_ALERT_FLAG_FREE_STRUCT; - + a->alert_type = KHUI_ALERTTYPE_NONE; + khui_context_create(&a->ctx, KHUI_SCOPE_NONE, + NULL, KCDB_CREDTYPE_INVALID, + NULL); + khui_alert_hold(a); EnterCriticalSection(&cs_alerts); LPUSH(&kh_alerts, a); @@ -253,6 +258,57 @@ khui_alert_add_command(khui_alert * alert, khm_int32 command_id) return rv; } +KHMEXP khm_int32 KHMAPI +khui_alert_set_type(khui_alert * alert, khui_alert_type alert_type) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + alert->alert_type = alert_type; + LeaveCriticalSection(&cs_alerts); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_set_ctx(khui_alert * alert, + khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + khui_context_release(&alert->ctx); + khui_context_create(&alert->ctx, + scope, + identity, + cred_type, + cred); + LeaveCriticalSection(&cs_alerts); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_get_response(khui_alert * alert) +{ + khm_int32 response = 0; + + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + response = alert->response; + LeaveCriticalSection(&cs_alerts); + + return response; +} + KHMEXP khm_int32 KHMAPI khui_alert_show(khui_alert * alert) { @@ -347,6 +403,9 @@ free_alert(khui_alert * alert) alert->suggestion = NULL; alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST; } + + khui_context_release(&alert->ctx); + if(alert->flags & KHUI_ALERT_FLAG_FREE_STRUCT) { alert->flags &= ~KHUI_ALERT_FLAG_FREE_STRUCT; alert->magic = 0; diff --git a/src/windows/identity/uilib/creddlg.c b/src/windows/identity/uilib/creddlg.c index 1d36d3c5b..c89a44620 100644 --- a/src/windows/identity/uilib/creddlg.c +++ b/src/windows/identity/uilib/creddlg.c @@ -418,6 +418,9 @@ cw_free_prompts(khui_new_creds * c) KHMEXP khm_int32 KHMAPI khui_cw_clear_prompts(khui_new_creds * c) { + /* the WMNC_CLEAR_PROMPT message needs to be sent before freeing + the prompts, because the prompts structure still holds the + window handles for the custom prompt controls. */ SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY, MAKEWPARAM(0,WMNC_CLEAR_PROMPTS), (LPARAM) c); diff --git a/src/windows/identity/uilib/intaction.h b/src/windows/identity/uilib/intaction.h index 2b4a66a1f..44348b9bb 100644 --- a/src/windows/identity/uilib/intaction.h +++ b/src/windows/identity/uilib/intaction.h @@ -41,4 +41,68 @@ typedef struct tag_khui_ui_callback_data { #define KHUI_UICBDATA_MAGIC 0x8a08572a +/*! \addtogroup khui_actions +@{ */ + +/*! \brief An action */ +typedef struct tag_khui_action { + khm_int32 cmd; /*!< action identifier */ + khm_int32 type; /*!< combination of KHUI_ACTIONTYPE_* */ + wchar_t * name; /*!< name for named actions. NULL if + not named. */ + + /* The following fields are only for use by NetIDMgr */ + khm_int16 ib_normal; /*!< (internal) normal bitmap (index) (toolbar sized icon) */ + khm_int16 ib_hot; /*!< (internal) hot bitmap (index) (toolbar sized icon) */ + khm_int16 ib_disabled; /*!< (internal) disabled bitmap (index) (toolbar sized icon) */ + + khm_int16 ib_icon; /*!< (internal) index of small (16x16) icon (for menu) (small icon) */ + khm_int16 ib_icon_dis; /*!< (internal) index of disabled (greyed) icon (small icon) */ + + khm_int16 is_caption; /*!< (internal) index of string resource for caption */ + khm_int16 is_tooltip; /*!< (internal) same for description / tooltip */ + khm_int16 ih_topic; /*!< (internal) help topic */ + + /* The following fields are specified for custom actions */ + wchar_t * caption; /*!< Caption (localized) (limited by + KHUI_MAXCCH_SHORT_DESC). The + caption is used for representing the + action in menus and toolbars. */ + wchar_t * tooltip; /*!< Tooltip (localized) (limited by + KHUI_MAXCCH_SHORT_DESC). If this is + specified, whenever the user hovers + over the menu item or toolbar button + representing the action, the tooltip + will be displayed either on a + tooltip window or in the status + bar. */ + khm_handle listener; /*!< Listener of this action. Should be + a handle to a message + subscription. When the action is + invoked, a message of type + ::KMSG_ACT and subtype + ::KMSG_ACT_ACTIVATE will be posted + to this subscriber. The \a uparam + parameter of the message will have + the identifier of the action. */ + void * data; /*!< User data for custom action. This + field is not used by the UI library. + It is reserved for plugins to store + data that is specific for this + action. The data that's passed in + in the \a userdata parameter to + khui_action_create() will be stored + here and can be retrieved by calling + khui_action_get_data(). */ + void * reserved1; /*!< Reserved. */ + void * reserved2; /*!< Reserved. */ + void * reserved3; /*!< Reserved. */ + + /* For all actions */ + int state; /*!< current state. combination of + KHUI_ACTIONSTATE_* */ +} khui_action; + +/*@}*/ + #endif diff --git a/src/windows/identity/uilib/khaction.h b/src/windows/identity/uilib/khaction.h index e8f6cd64a..b36ea098c 100644 --- a/src/windows/identity/uilib/khaction.h +++ b/src/windows/identity/uilib/khaction.h @@ -32,64 +32,8 @@ /*! \defgroup khui_actions Actions @{*/ -/*! \brief An action */ -typedef struct tag_khui_action { - khm_int32 cmd; /*!< action identifier */ - khm_int32 type; /*!< combination of KHUI_ACTIONTYPE_* */ - wchar_t * name; /*!< name for named actions. NULL if - not named. */ - - /* The following fields are only for use by NetIDMgr */ - khm_int16 ib_normal; /*!< (internal) normal bitmap (index) (toolbar sized icon) */ - khm_int16 ib_hot; /*!< (internal) hot bitmap (index) (toolbar sized icon) */ - khm_int16 ib_disabled; /*!< (internal) disabled bitmap (index) (toolbar sized icon) */ - - khm_int16 ib_icon; /*!< (internal) index of small (16x16) icon (for menu) (small icon) */ - khm_int16 ib_icon_dis; /*!< (internal) index of disabled (greyed) icon (small icon) */ - - khm_int16 is_caption; /*!< (internal) index of string resource for caption */ - khm_int16 is_tooltip; /*!< (internal) same for description / tooltip */ - khm_int16 ih_topic; /*!< (internal) help topic */ - - /* The following fields are specified for custom actions */ - wchar_t * caption; /*!< Caption (localized) (limited by - KHUI_MAXCCH_SHORT_DESC). The - caption is used for representing the - action in menus and toolbars. */ - wchar_t * tooltip; /*!< Tooltip (localized) (limited by - KHUI_MAXCCH_SHORT_DESC). If this is - specified, whenever the user hovers - over the menu item or toolbar button - representing the action, the tooltip - will be displayed either on a - tooltip window or in the status - bar. */ - khm_handle listener; /*!< Listener of this action. Should be - a handle to a message - subscription. When the action is - invoked, a message of type - ::KMSG_ACT and subtype - ::KMSG_ACT_ACTIVATE will be posted - to this subscriber. The \a uparam - parameter of the message will have - the identifier of the action. */ - void * data; /*!< User data for custom action. This - field is not used by the UI library. - It is reserved for plugins to store - data that is specific for this - action. The data that's passed in - in the \a userdata parameter to - khui_action_create() will be stored - here and can be retrieved by calling - khui_action_get_data(). */ - void * reserved1; /*!< Reserved. */ - void * reserved2; /*!< Reserved. */ - void * reserved3; /*!< Reserved. */ - - /* For all actions */ - int state; /*!< current state. combination of - KHUI_ACTIONSTATE_* */ -} khui_action; +struct tag_khui_action; +typedef struct tag_khui_action khui_action; /*! \brief Unknown action type @@ -343,6 +287,40 @@ extern int khui_n_all_menus; /* functions */ +/*! \brief Refresh the global action table + + Changes to system menus and toolbars may not be immediately + reflected in the user interface. Calling this function forces the + UI to reparse the action tables and menus and refresh the + application menu bar and toolbars. + + */ +KHMEXP void KHMAPI +khui_refresh_actions(void); + +/*! \brief Lock the action and menu tables + + This function, along with khui_action_unlock() is used to prevent + changes from being made to shared menus and actions while they are + being updated. In particular, changes to shared menus usually + need to be done in a batch and may suffer corruption of other + threads access or modify the menu while one thread is updating it. + Operations on shared menus should always be done with the actions + locked. +*/ +KHMEXP void KHMAPI +khui_action_lock(void); + +/*! \brief Unlock the action and menu tables + + Unlocks the action and menu tables after a call to + khui_action_lock(). + + \see khui_action_lock() + */ +KHMEXP void KHMAPI +khui_action_unlock(void); + /*! \brief Create a new menu Creates a new menu. The returned data structure must be freed by @@ -887,9 +865,28 @@ KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref); /*! \brief Create a new action + Creates a new custom action. The created custom action can be + added to menus, toolbars and can be triggered by + khui_action_trigger(). + + When the action is triggered as a result of the user selecting a + menu item, a toolbar item or as a result of calling + khui_action_trigger(), the subscription identified by \a hsub will + received a message of type ::KMSG_ACT, subtype + ::KMSG_ACT_ACTIVATE. The \a uparam for the message will be the + action identifier that was returned by khui_action_create(). The + \a vparam of the message will currently be set to \a NULL. + + Actions can optionally be named. The name is not actively used by + the Network Identity Manager framework, but can be used to label + actions so that they can be looked up later using + khui_find_named_action(). + \param[in] name Name for a named action. The name must be unique - among all registered actions. (limited by KHUI_MAXCCH_NAME) + among all registered actions. (limited by KHUI_MAXCCH_NAME). (Optional. Set to NULL if the action is not a named action.) + See \a note below for additional restrictions on the name of + the action. \param[in] caption The localized caption for the action. This will be shown in menus, toolbars and buttons when the action @@ -900,11 +897,7 @@ KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref); by KHUI_MAXCCH_SHORT_DESC) (Optional, set to NULL if there is no tooltip associated with the action) - \param[in] hsub The subscription that is notified when the action - is triggered. (Optional) The subscription can be created with - kmq_create_subscription(). The handle will be released when - it is no longer needed. Hence, the caller should not release - it. + \param[in] userdata A custom value. \param[in] type The type of the action. Currently it should be set to either ::KHUI_ACTIONTYPE_TRIGGER or @@ -912,7 +905,11 @@ KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref); initial state will be unchecked. Use khui_check_action() function to change the checked state of the action. - \param[in] userdata A custom value. + \param[in] hsub The subscription that is notified when the action + is triggered. (Optional) The subscription must be created with + kmq_create_subscription(). The handle will be released when + it is no longer needed. Hence, the caller should not release + it. \return The identifier of the new action or zero if the action could not be created. diff --git a/src/windows/identity/uilib/khalerts.h b/src/windows/identity/uilib/khalerts.h index f10ffa198..2bbc7d0da 100644 --- a/src/windows/identity/uilib/khalerts.h +++ b/src/windows/identity/uilib/khalerts.h @@ -37,86 +37,10 @@ /*!\defgroup khui_alert Alerter and Error Reporting @{*/ -#define KHUI_MAX_ALERT_COMMANDS 4 - -/*! \brief An alert - - Describes an alert message that will be shown to the user in a - variety of ways depending on the state of the NetIDMgr - application. - */ -typedef struct tag_khui_alert { - khm_int32 magic; /*!< Magic number. Always set to - KHUI_ALERT_MAGIC */ - - khm_int32 severity; /*!< The severity of the alert. One - of KHERR_ERROR, KHERR_WARNING or - KHERR_INFO. The default is - KHERR_INFO. Do not set directly. - Use khui_alert_set_severity(). */ - - khm_int32 alert_commands[KHUI_MAX_ALERT_COMMANDS]; - /*!< The command buttons associated - with the alert. Use - khui_alert_add_command() to add a - command. The buttons will appear in - the order in which they were added. - The first button will be the - default. Each command should be a - known action identifier. */ - khm_int32 n_alert_commands; - - wchar_t * title; /*!< The title of the alert. Subject - to ::KHUI_MAXCCH_TITLE. Use - khui_alert_set_title() to set. Do - not modify directly. */ - - wchar_t * message; /*!< The main message of the alert. - Subject to ::KHUI_MAXCCH_MESSAGE. - Use khui_alert_set_message() to - set. Do not modify direcly. */ - - wchar_t * suggestion; /*!< A suggestion. Appears below - the message text. Use - khui_alert_set_suggestion() to - set. Do not modify directly. */ - -#ifdef _WIN32 - POINT target; -#endif - - khm_int32 flags; /*!< combination of - ::khui_alert_flags. Do not modify - directly. */ - - kherr_context * err_context; - /*!< If non-NULL at the time the alert - window is shown, this indicates that - the alert window should provide an - error viewer for the given error - context. */ +struct tag_khui_alert; +typedef struct tag_khui_alert khui_alert; - kherr_event * err_event; - /*!< If non-NULL at the time the alert - window is shown, this indicates that - the alert window should provide an - error viewer for the given error - event. If an \a err_context is also - given, the error viewer for the - context will be below this error. */ - - khm_int32 response; - /*!< Once the alert is displayed to - the user, when the user clicks one - of the command buttons, the command - ID will be assigned here. */ - - int refcount; /* internal */ - - LDCL(struct tag_khui_alert); /* internal */ -} khui_alert; - -#define KHUI_ALERT_MAGIC 0x48c39ce9 +#define KHUI_MAX_ALERT_COMMANDS 4 /*! \brief Maximum number of characters in title including terminating NULL */ @@ -186,6 +110,27 @@ enum khui_alert_flags { /*!< Bit mask of flags that can be set by khui_alert_set_flags() */ }; +/*! \brief Alert types + + These types can be set with khui_alert_set_type() to indicate + which type of alert this is. The types defined here are + identified by the Network Identity Manager and will receive + special handling whereever appropriate. + + The type is a hint to the application and will not guarantee a + particular behavior. + */ +typedef enum tag_khui_alert_types { + KHUI_ALERTTYPE_NONE = 0, /*!< No specific alert type */ + KHUI_ALERTTYPE_PLUGIN, /*!< Plug-in or module load related + alert */ + KHUI_ALERTTYPE_EXPIRE, /*!< Credential or identity expiration + warning */ + KHUI_ALERTTYPE_RENEWFAIL, /*!< Failed to renew credentials */ + KHUI_ALERTTYPE_ACQUIREFAIL, /*!< Failed to acquire credentials */ + KHUI_ALERTTYPE_CHPW, /*!< Failed to change password */ +} khui_alert_type; + /*! \brief Create an empty alert object The returned result is a held pointer to a ::khui_alert object. @@ -267,12 +212,39 @@ khui_alert_clear_commands(khui_alert * alert); /*! \brief Add a command to an alert object - The command ID should be a valid registered command. + The command ID should be a valid registered action. */ KHMEXP khm_int32 KHMAPI khui_alert_add_command(khui_alert * alert, khm_int32 command_id); +/*! \brief Set the type of alert + */ +KHMEXP khm_int32 KHMAPI +khui_alert_set_type(khui_alert * alert, + khui_alert_type type); + +/*! \brief Set the action context for the alert */ +KHMEXP khm_int32 KHMAPI +khui_alert_set_ctx(khui_alert * alert, + khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred); + +/*! \brief Get the response code from an alert + + Once an alert has been displayed to the user, the user may choose + a command from the list of commands provided in the alert (see + khui_alert_add_command() ). This function can retrieve the + selected command from the alert. + + \return The selected command or \a 0 if no commands were selected. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_get_response(khui_alert * alert); + + /*! \brief Display an alert The alert must have a valid \a severity, \a title and a \a message @@ -317,7 +289,6 @@ khui_alert_show(khui_alert * alert); This function always opens an alert window (never shows a balloon). - \note Should only be called from the UI thread. */ KHMEXP khm_int32 KHMAPI khui_alert_show_modal(khui_alert * alert); @@ -377,7 +348,7 @@ khui_alert_release(khui_alert * alert); disallows access to all other alerts as well. \note Calling khui_alert_lock() is only necessary if you are - modifying the ::khui_alert structure directly. Calling any of + accessing the ::khui_alert structure directly. Calling any of the khui_alert_* functions to modify the alert does not require obtaining a lock, as they perform synchronization internally. diff --git a/src/windows/identity/uilib/khnewcred.h b/src/windows/identity/uilib/khnewcred.h index 1785d5972..45a565cde 100644 --- a/src/windows/identity/uilib/khnewcred.h +++ b/src/windows/identity/uilib/khnewcred.h @@ -146,6 +146,10 @@ enum khui_wm_nc_notifications { WMNC_ADD_CONTROL_ROW, /*!< Add a row of controls to a new cred dialog. This is an internal message. */ + + WMNC_UPDATE_LAYOUT, + /*!< Update the layout of a dialog or window. This is an internal + message. */ }; /*! \brief Plugins can use WMNC_NOTIFY message codes from here on up @@ -551,13 +555,6 @@ typedef struct tag_khui_new_creds_by_type { /*! \brief Height of a new creds dialog panel in dialog units*/ #define NCDLG_HEIGHT 166 -/*! \brief Width of the button bar in dialog units */ -#define NCDLG_BBAR_WIDTH 60 -/*! \brief Height of a tab button in dialog units */ -#define NCDLG_TAB_HEIGHT 15 -/*! \brief Width of a tab button in dialog units */ -#define NCDLG_TAB_WIDTH 60 - /*! \brief A custom prompt */ typedef struct tag_khui_new_creds_prompt { khm_size index; /*!< Set to the zero based index @@ -944,7 +941,7 @@ khui_cw_set_response(khui_new_creds * c, /*! \brief Check whether a specified credential type panel succeeded - This is called during the processing of KMSG_CRED_DIALOG_PROCESS + This is called during the processing of ::KMSG_CRED_DIALOG_PROCESS to determine whether a specified credential type succeeded in obtaining credentials. The credential type that is being queried should have also been listed as a dependency when adding the diff --git a/src/windows/identity/uilib/khuidefs.h b/src/windows/identity/uilib/khuidefs.h index 845f781d2..9b3855614 100644 --- a/src/windows/identity/uilib/khuidefs.h +++ b/src/windows/identity/uilib/khuidefs.h @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -45,6 +44,10 @@ #include #include +#ifdef NOEXPORT +#include +#endif + #include /*! \internal */ @@ -115,9 +118,23 @@ typedef khm_int32 any windows it wishes to create and interact with the user directly. - Note that when the plug-in creates any windows, it should specify - the window handle provided via the \a hwnd_main_wnd parameter as - the parent window. + The callback function would be called synchronously. + khui_request_UI_callback() will not return until the user + interface processes the request and calls the callback function. + The return code of khui_request_UI_callback() will be the return + code of the callback. + + \param[in] cb The callback function which will be called from the + user interface thread. + + \param[in] rock An arbitrary parameter which will be passed into + the callback function. + + \return The return value of \a cb. + + \note When the plug-in creates any windows, it should specify the + window handle provided via the \a hwnd_main_wnd parameter as + the parent window. \see ::khm_ui_callback */ diff --git a/src/windows/identity/uilib/rescache.c b/src/windows/identity/uilib/rescache.c index 6d5259142..b84608a8e 100644 --- a/src/windows/identity/uilib/rescache.c +++ b/src/windows/identity/uilib/rescache.c @@ -24,6 +24,8 @@ /* $Id$ */ +#define NOEXPORT + #include #include diff --git a/src/windows/identity/uilib/uibind.c b/src/windows/identity/uilib/uibind.c index ac5e13626..11da4e4bf 100644 --- a/src/windows/identity/uilib/uibind.c +++ b/src/windows/identity/uilib/uibind.c @@ -53,6 +53,7 @@ khui_request_UI_callback(khm_ui_callback cb, void * rock) { MAKEWPARAM(KHUI_ACTION_UICB, 0), (LPARAM) &cbdata); - return KHM_ERROR_SUCCESS; + return cbdata.rv; } + diff --git a/src/windows/identity/util/hashtable.c b/src/windows/identity/util/hashtable.c index 1a4233d51..ddf7b1bf8 100644 --- a/src/windows/identity/util/hashtable.c +++ b/src/windows/identity/util/hashtable.c @@ -63,10 +63,13 @@ KHMEXP void KHMAPI hash_del_hashtable(hashtable * h) { } } + if (h->bins) + PFREE(h->bins); + PFREE(h); } -KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data) { +KHMEXP void KHMAPI hash_add(hashtable * h, const void * key, void * data) { int hv; hash_bin * b; @@ -97,7 +100,7 @@ KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data) { } } -KHMEXP void KHMAPI hash_del(hashtable * h, void * key) { +KHMEXP void KHMAPI hash_del(hashtable * h, const void * key) { hash_bin * b; int hv; @@ -117,7 +120,7 @@ KHMEXP void KHMAPI hash_del(hashtable * h, void * key) { } } -KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key) { +KHMEXP void * KHMAPI hash_lookup(hashtable * h, const void * key) { hash_bin * b; int hv; @@ -135,7 +138,7 @@ KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key) { return NULL; } -KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key) { +KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, const void * key) { hash_bin * b; int hv; diff --git a/src/windows/identity/util/hashtable.h b/src/windows/identity/util/hashtable.h index fc7d829f2..1a3fb4b86 100644 --- a/src/windows/identity/util/hashtable.h +++ b/src/windows/identity/util/hashtable.h @@ -83,7 +83,7 @@ typedef void (*del_ref_function_t)(const void *key, void *data); typedef struct tag_hash_bin { void * data; - void * key; + const void * key; LDCL(struct tag_hash_bin); } hash_bin; @@ -147,7 +147,7 @@ KHMEXP void KHMAPI hash_del_hashtable(hashtable * h); \note Not thread-safe. Applications must serialize calls that reference the same hashtable. */ -KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data); +KHMEXP void KHMAPI hash_add(hashtable * h, const void * key, void * data); /*! \brief Delete an object from a hashtable @@ -161,7 +161,7 @@ KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data); \note Not thread-safe. Applications must serialize calls that reference the same hashtable. */ -KHMEXP void KHMAPI hash_del(hashtable * h, void * key); +KHMEXP void KHMAPI hash_del(hashtable * h, const void * key); /*! \brief Resolve and association @@ -175,7 +175,7 @@ KHMEXP void KHMAPI hash_del(hashtable * h, void * key); \note Not thread-safe. Applications must serialize calls that reference the same hashtable. */ -KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key); +KHMEXP void * KHMAPI hash_lookup(hashtable * h, const void * key); /*! \brief Check for the presence of an association @@ -190,7 +190,7 @@ KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key); \note Not thead-safe. Application must serialize calls that reference the same hashtable. */ -KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key); +KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, const void * key); /*! \brief Compute a hashvalue for a unicode string @@ -202,6 +202,10 @@ KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key); \param[in] str A pointer to a NULL terminated wchar_t string cast as (void *). + + \note This function does not check the length of the string \a + str. If the string is not \a NULL terminated, the behavior is + undefined. */ KHMEXP khm_int32 hash_string(const void *str); @@ -214,6 +218,10 @@ KHMEXP khm_int32 hash_string(const void *str); as (void *). \param[in] vs2 A pointer to a NULL terminated wchar_t string cast as (void *). + + \note This function does not check the length of the strings \a + vs1 and \a vs2. If the strings are not NULL terminated, the + behavior is undefined. */ KHMEXP khm_int32 hash_string_comp(const void *vs1, const void *vs2); diff --git a/src/windows/identity/util/perfstat.c b/src/windows/identity/util/perfstat.c index 4d7e8fa7c..cc27d93bd 100644 --- a/src/windows/identity/util/perfstat.c +++ b/src/windows/identity/util/perfstat.c @@ -37,7 +37,7 @@ #define HASHPTR(p) (((size_t) (p)) % HASHSIZE) typedef struct tag_allocation { - char file[8]; + const char * file; int line; size_t size; void * ptr; @@ -54,10 +54,49 @@ static allocation * next_alloc = NULL; static size_t idx_next_alloc = 0; static allocation * free_alloc = NULL; +typedef struct tag_thread_info { +#ifdef _WIN32 + DWORD thread; +#else +#error Unsupported platform +#endif + wchar_t name[128]; + wchar_t creator[128]; + + const char * file; + int line; + + LDCL(struct tag_thread_info); +} thread_info; + +static thread_info * threads = NULL; + +static hashtable fn_hash; + static CRITICAL_SECTION cs_alloc; static LONG ctr = 0; static int perf_ready = 0; +static DWORD init_thread = 0; + +static khm_int32 hash_stringA(const void * vs) { + /* DJB algorithm */ + + khm_int32 hv = 13331; + char * c; + + for (c = (char *) vs; *c; c++) { + hv = ((hv << 5) + hv) + (khm_int32) *c; + } + + return (hv & KHM_INT32_MAX); +} + +static khm_int32 hash_string_compA(const void * vs1, + const void * vs2) { + return strcmp((const char *) vs1, (const char *) vs2); +} + static void perf_once(void) { if (InterlockedIncrement(&ctr) == 1) { InitializeCriticalSection(&cs_alloc); @@ -68,9 +107,18 @@ static void perf_once(void) { idx_next_alloc = 0; free_alloc = NULL; + ZeroMemory(&fn_hash, sizeof(fn_hash)); + fn_hash.n = 13; + fn_hash.hash = hash_stringA; + fn_hash.comp = hash_string_compA; + fn_hash.bins = calloc(sizeof(hash_bin *), fn_hash.n); + perf_ready = 1; } else { - while(!perf_ready) { + DWORD this_thread = GetCurrentThreadId(); + + while(!perf_ready && + init_thread != this_thread) { Sleep(0); /* relinquish control to the thread that is initializing the alloc data. */ @@ -99,7 +147,7 @@ static allocation * get_allocation(void) { #define MAXCB_STR 32768 KHMEXP wchar_t * -perf_wcsdup(char * file, int line, const wchar_t * str) { +perf_wcsdup(const char * file, int line, const wchar_t * str) { size_t cb; wchar_t * dest; @@ -114,7 +162,7 @@ perf_wcsdup(char * file, int line, const wchar_t * str) { } KHMEXP char * -perf_strdup(char * file, int line, const char * str) { +perf_strdup(const char * file, int line, const char * str) { size_t cb; char * dest; @@ -129,7 +177,7 @@ perf_strdup(char * file, int line, const char * str) { } KHMEXP void * -perf_calloc(char * file, int line, size_t num, size_t size) { +perf_calloc(const char * file, int line, size_t num, size_t size) { void * ptr; size_t tsize; @@ -145,10 +193,11 @@ perf_calloc(char * file, int line, size_t num, size_t size) { } KHMEXP void * -perf_malloc(char * file, int line, size_t s) { +perf_malloc(const char * file, int line, size_t s) { allocation * a; void * ptr; size_t h; + char * fn_copy = NULL; perf_once(); @@ -164,7 +213,33 @@ perf_malloc(char * file, int line, size_t s) { if (file[0] == '.' && file[1] == '\\') file += 2; - StringCbCopyA(a->file, sizeof(a->file), file); + fn_copy = hash_lookup(&fn_hash, file); + if (fn_copy == NULL) { + + size_t cblen = 0; + if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char), + &cblen))) + fn_copy = NULL; + else { + fn_copy = malloc(cblen + sizeof(char)); + if (fn_copy) { + hash_bin * b; + int hv; + + StringCbCopyA(fn_copy, cblen + sizeof(char), file); + + hv = fn_hash.hash(fn_copy) % fn_hash.n; + + b = malloc(sizeof(*b)); + b->data = fn_copy; + b->key = fn_copy; + LINIT(b); + LPUSH(&fn_hash.bins[hv], b); + } + } + } + + a->file = fn_copy; a->line = line; a->size = s; a->ptr = ptr; @@ -181,7 +256,7 @@ perf_malloc(char * file, int line, size_t s) { } KHMEXP void * -perf_realloc(char * file, int line, void * data, size_t s) { +perf_realloc(const char * file, int line, void * data, size_t s) { void * n_data; allocation * a; size_t h; @@ -237,41 +312,100 @@ perf_free (void * b) { LeaveCriticalSection(&cs_alloc); } -KHMEXP void -perf_dump(char * file) { - FILE * f; +KHMEXP void KHMAPI +perf_dump(FILE * f) { size_t i; allocation * a; size_t total = 0; + thread_info * t; perf_once(); EnterCriticalSection(&cs_alloc); -#if _MSC_VER >= 1400 - if (fopen_s(&f, file, "w")) - return; -#else - f = fopen(file, "w"); - if (!f) - return; -#endif - fprintf(f, "Leaked allocations list ....\n"); - fprintf(f, "File\tLine\tThread\tSize\n"); + fprintf(f, "p00\t*** Threads ***\n"); + fprintf(f, "p00\tFile\tLine\tThread\tName\tCreated by\n"); + + for (t = threads; t; t = LNEXT(t)) { + fprintf(f, "p01\t%s\t%6d\t%6d\t%S\t%S\n", + t->file, t->line, t->thread, + t->name, t->creator); + } + + fprintf(f, "p02\t--- End Threads ---\n"); + + fprintf(f, "p10\t*** Leaked allocations list ***\n"); + fprintf(f, "p11\tFile\tLine\tThread\tSize\tAddress\n"); for (i=0; i < HASHSIZE; i++) { for (a = ht[i]; a; a = LNEXT(a)) { - fprintf(f, "%s\t%6d\t%6d\t%6d\n", a->file, a->line, - a->thread, a->size); + fprintf(f, "p12\t%s\t%6d\t%6d\t%6d\t0x%p\n", a->file, a->line, + a->thread, a->size, a->ptr); total += a->size; } } - fprintf(f, "----------------------------------------\n"); - fprintf(f, "Total\t\t%d\n", total); - fprintf(f, "----------------- End ------------------\n"); + fprintf(f, "p20\t----------------------------------------\n"); + fprintf(f, "p21\tTotal\t\t%d\n", total); + fprintf(f, "p22\t----------------- End ------------------\n"); + + LeaveCriticalSection(&cs_alloc); +} + +KHMEXP void +perf_set_thread_desc(const char * file, int line, + const wchar_t * name, const wchar_t * creator) { + thread_info * t; + char * fn_copy; + + perf_once(); + + t = malloc(sizeof(*t)); + ZeroMemory(t, sizeof(*t)); + +#ifdef _WIN32 + t->thread = GetCurrentThreadId(); +#else +#error Unsupported platform +#endif + + StringCbCopy(t->name, sizeof(t->name), name); + if (creator) + StringCbCopy(t->creator, sizeof(t->creator), creator); + + if (file[0] == '.' && file[1] == '\\') + file += 2; + + EnterCriticalSection(&cs_alloc); + + fn_copy = hash_lookup(&fn_hash, file); + if (fn_copy == NULL) { + size_t cblen = 0; + if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char), + &cblen))) + fn_copy = NULL; + else { + fn_copy = malloc(cblen + sizeof(char)); + if (fn_copy) { + hash_bin * b; + int hv; + + StringCbCopyA(fn_copy, cblen + sizeof(char), file); + + hv = fn_hash.hash(fn_copy) % fn_hash.n; + + b = malloc(sizeof(*b)); + b->data = fn_copy; + b->key = fn_copy; + LINIT(b); + LPUSH(&fn_hash.bins[hv], b); + } + } + } - fclose(f); + t->file = fn_copy; + t->line = line; + LPUSH(&threads, t); LeaveCriticalSection(&cs_alloc); } diff --git a/src/windows/identity/util/perfstat.h b/src/windows/identity/util/perfstat.h index d8d5951b3..a4a0f2750 100644 --- a/src/windows/identity/util/perfstat.h +++ b/src/windows/identity/util/perfstat.h @@ -37,6 +37,7 @@ #define PDUMP(f) perf_dump(f) #define PWCSDUP(s) perf_wcsdup(__FILE__,__LINE__,s) #define PSTRDUP(s) perf_strdup(__FILE__,__LINE__,s) +#define PDESCTHREAD(n,c) perf_set_thread_desc(__FILE__,__LINE__,n,c); #else #define PMALLOC(s) malloc(s) #define PCALLOC(n,s) calloc(n,s) @@ -45,27 +46,29 @@ #define PDUMP(f) ((void) 0) #define PWCSDUP(s) wcsdup(s) #define PSTRDUP(s) strdup(s) +#define PDESCTHREAD(n,c) #endif KHMEXP void * -perf_malloc(char * file, int line, size_t s); +perf_malloc(const char * file, int line, size_t s); KHMEXP void * -perf_realloc(char * file, int line, void * data, size_t s); +perf_realloc(const char * file, int line, void * data, size_t s); KHMEXP void perf_free (void * b); -KHMEXP void -perf_dump (char * filename); - KHMEXP wchar_t * -perf_wcsdup(char * file, int line, const wchar_t * str); +perf_wcsdup(const char * file, int line, const wchar_t * str); KHMEXP char * -perf_strdup(char * file, int line, const char * str); +perf_strdup(const char * file, int line, const char * str); KHMEXP void * -perf_calloc(char * file, int line, size_t num, size_t size); +perf_calloc(const char * file, int line, size_t num, size_t size); + +KHMEXP void +perf_set_thread_desc(const char * file, int line, + const wchar_t * name, const wchar_t * creator); #endif