Native window decorations: avoid using C-runtime, which reduces the DLL size from 100kb to 8kb

This commit is contained in:
Karl Tauber
2021-03-06 12:01:49 +01:00
parent 144d65c776
commit 8734b062dc
7 changed files with 308 additions and 17 deletions

2
.gitignore vendored
View File

@@ -9,3 +9,5 @@ out/
*.iml
*.ipr
*.iws
.vs/
.vscode/

View File

@@ -55,7 +55,7 @@ library {
compilerArgs.addAll( toolChain.map {
when( it ) {
is Gcc, is Clang -> listOf( "-O2" )
is VisualCpp -> listOf( "/O2" )
is VisualCpp -> listOf( "/O2", "/Zl", "/GS-" )
else -> emptyList()
}
} )
@@ -70,8 +70,8 @@ library {
val jawt = "${org.gradle.internal.jvm.Jvm.current().javaHome}/lib/jawt"
linkerArgs.addAll( toolChain.map {
when( it ) {
is Gcc, is Clang -> listOf( "-l${jawt}", "-lUser32", "-lshell32", "-lAdvAPI32" )
is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "shell32.lib", "AdvAPI32.lib" )
is Gcc, is Clang -> listOf( "-l${jawt}", "-lUser32", "-lshell32", "-lAdvAPI32", "-lKernel32" )
is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "/NODEFAULTLIB" )
else -> emptyList()
}
} )

View File

@@ -14,12 +14,14 @@
* limitations under the License.
*/
// avoid inlining of printf()
#define _NO_CRT_STDIO_INLINE
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include <jawt.h>
#include <jawt_md.h>
#include <map>
#include "FlatWndProc.h"
#include "com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h"
@@ -50,7 +52,7 @@ jmethodID FlatWndProc::onNcHitTestMID;
jmethodID FlatWndProc::isFullscreenMID;
jmethodID FlatWndProc::fireStateChangedLaterOnceMID;
std::map<HWND, FlatWndProc*> FlatWndProc::hwndMap = std::map<HWND, FlatWndProc*>();
HWNDMap* FlatWndProc::hwndMap;
//---- class FlatWndProc methods ----------------------------------------------
@@ -63,21 +65,25 @@ FlatWndProc::FlatWndProc() {
}
HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) {
initIDs( env, obj );
initIDs( env, obj );
if( initialized < 0 )
return 0;
if( initialized < 0 )
return 0;
// create HWND map
if( hwndMap == NULL )
hwndMap = new HWNDMap();
// get window handle
HWND hwnd = getWindowHandle( env, window );
if( hwnd == NULL || hwndMap.count( hwnd ) > 0 )
if( hwnd == NULL || hwndMap->get( hwnd ) != NULL )
return 0;
FlatWndProc* fwp = new FlatWndProc();
env->GetJavaVM( &fwp->jvm );
fwp->obj = env->NewGlobalRef( obj );
fwp->hwnd = hwnd;
hwndMap[hwnd] = fwp;
hwndMap->put( hwnd, fwp );
// replace window procedure
fwp->defaultWndProc = reinterpret_cast<WNDPROC>(
@@ -90,11 +96,14 @@ HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) {
}
void FlatWndProc::uninstall( JNIEnv *env, jobject obj, HWND hwnd ) {
if( hwnd == NULL || hwndMap.count( hwnd ) == 0 )
return;
if( hwnd == NULL )
return;
FlatWndProc* fwp = hwndMap[hwnd];
hwndMap.erase( hwnd );
FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd );
if( fwp == NULL )
return;
hwndMap->remove( hwnd );
// restore original window procedure
::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) fwp->defaultWndProc );
@@ -132,7 +141,7 @@ void FlatWndProc::updateFrame() {
}
LRESULT CALLBACK FlatWndProc::StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
FlatWndProc* fwp = hwndMap[hwnd];
FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd );
return fwp->WindowProc( hwnd, uMsg, wParam, lParam );
}
@@ -175,7 +184,7 @@ LRESULT FlatWndProc::WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPara
// cleanup
getEnv()->DeleteGlobalRef( obj );
hwndMap.erase( hwnd );
hwndMap->remove( hwnd );
delete this;
// call original AWT window procedure because it may fire window closed event in AwtWindow::WmDestroy()

View File

@@ -15,6 +15,7 @@
*/
#include <windows.h>
#include "HWNDMap.h"
/**
* @author Karl Tauber
@@ -31,7 +32,7 @@ private:
static jmethodID isFullscreenMID;
static jmethodID fireStateChangedLaterOnceMID;
static std::map<HWND, FlatWndProc*> hwndMap;
static HWNDMap* hwndMap;
JavaVM* jvm;
JNIEnv* env; // attached to AWT-Windows/Win32 thread

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// avoid inlining of printf()
#define _NO_CRT_STDIO_INLINE
#include <stdio.h>
#include "HWNDMap.h"
#define DEFAULT_CAPACITY 20
#define INCREASE_CAPACITY 10
/**
* @author Karl Tauber
*/
class LOCK {
LPCRITICAL_SECTION lpCriticalSection;
public:
LOCK( LPCRITICAL_SECTION lpCriticalSection ) {
this->lpCriticalSection = lpCriticalSection;
::EnterCriticalSection( lpCriticalSection );
}
~LOCK() {
::LeaveCriticalSection( lpCriticalSection );
}
};
HWNDMap::HWNDMap() {
size = 0;
capacity = DEFAULT_CAPACITY;
table = new Entry[capacity];
::InitializeCriticalSection( &criticalSection );
// dump( "<init>" );
}
LPVOID HWNDMap::get( HWND key ) {
LOCK lock( &criticalSection );
int index = binarySearch( key );
return (index >= 0) ? table[index].value : NULL;
}
void HWNDMap::put( HWND key, LPVOID value ) {
LOCK lock( &criticalSection );
int index = binarySearch( key );
// printf( "put %p %p = %d --\n", key, value, index );
if( index >= 0 ) {
// key already in map --> replace
table[index].value = value;
} else {
// insert new key
ensureCapacity( size + 1 );
// make roor for new entry
index = -(index + 1);
for( int i = size - 1; i >= index; i-- )
table[i + 1] = table[i];
size++;
// insert entry
table[index].key = key;
table[index].value = value;
}
// dump( "put" );
}
void HWNDMap::remove( HWND key ) {
LOCK lock( &criticalSection );
// search for key
int index = binarySearch( key );
// printf( "remove %p = %d --\n", key, index );
if( index < 0 )
return;
// remove entry
for( int i = index + 1; i < size; i++ )
table[i - 1] = table[i];
size--;
// dump( "remove" );
}
int HWNDMap::binarySearch( HWND key ) {
int low = 0;
int high = size - 1;
while( low <= high ) {
int mid = (low + high) >> 1;
HWND midKey = table[mid].key;
int cmp = midKey - key;
if( cmp < 0 )
low = mid + 1;
else if( cmp > 0 )
high = mid - 1;
else
return mid;
}
return -(low + 1);
}
void HWNDMap::ensureCapacity( int minCapacity ) {
if( minCapacity <= capacity )
return;
// allocate new table
int newCapacity = minCapacity + INCREASE_CAPACITY;
Entry* newTable = new Entry[newCapacity];
// copy old table to new table
for( int i = 0; i < capacity; i++ )
newTable[i] = table[i];
// delete old table
delete table;
table = newTable;
capacity = newCapacity;
}
/*
void HWNDMap::dump( char* msg ) {
printf( "---- %s -----------------------\n", msg );
printf( "size %d\n", size );
printf( "capacity %d\n", capacity );
printf( "table %p\n", table );
for( int i = 0; i < capacity; i++ )
printf( " %d: %p - %p %s\n", i, table[i].key, table[i].value, i >= size ? "UNUSED" : "" );
}
*/

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <windows.h>
/**
* A simple map that uses a sorted array to store key/value pairs.
*
* @author Karl Tauber
*/
struct Entry
{
HWND key;
LPVOID value;
};
class HWNDMap
{
private:
int size; // used entries in table
int capacity; // total size of table
Entry* table;
// used to synchronize to make it thread safe
CRITICAL_SECTION criticalSection;
public:
HWNDMap();
LPVOID get( HWND key );
void put( HWND key, LPVOID value );
void remove( HWND key );
private:
int binarySearch( HWND key );
void ensureCapacity( int newCapacity );
// void dump( char* msg );
};

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// avoid inlining of printf()
#define _NO_CRT_STDIO_INLINE
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
/**
* Methods that replace C-runtime methods and allow linking/running without C-runtime.
*
* WARNING: Constructors/destructors of static objects are not invoked!
*
* https://documentation.help/Far-Manager/msdnmag-issues-01-01-hood-default.aspx.html
* www.catch22.net/tuts/win32/reducing-executable-size#the-c-runtime-and-default-libraries
* https://www.mvps.org/user32/nocrt.html
*
* see also LIBCTINY on "Downloads" page here: http://www.wheaty.net/
* or https://github.com/leepa/libctiny
*
* @author Karl Tauber
*/
extern "C"
BOOL WINAPI _DllMainCRTStartup( HINSTANCE instance, DWORD reason, LPVOID reserved ) {
return TRUE;
}
void* __cdecl operator new( size_t cb ) {
return ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, cb );
}
void* __cdecl operator new[]( size_t cb ) {
return ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, cb );
}
void __cdecl operator delete( void* pv, size_t cb ) {
if( pv != NULL )
::HeapFree( ::GetProcessHeap(), 0, pv );
}
/*
extern "C"
int __cdecl printf( const char* format, ... ) {
char szBuff[1024];
int retValue;
DWORD cbWritten;
va_list argptr;
va_start( argptr, format );
retValue = wvsprintfA( szBuff, format, argptr );
va_end( argptr );
WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), szBuff, retValue, &cbWritten, NULL );
return retValue;
}
*/