window.c 92 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540
  1. /*
  2. * Copyright 2001 Eric Pouech
  3. * Copyright 2020 Jacek Caban for CodeWeavers
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  18. */
  19. #define NONAMELESSUNION
  20. #include <stdlib.h>
  21. #include "conhost.h"
  22. #include <commctrl.h>
  23. #include <winreg.h>
  24. #include "wine/debug.h"
  25. WINE_DEFAULT_DEBUG_CHANNEL(console);
  26. #define WM_UPDATE_CONFIG (WM_USER + 1)
  27. enum update_state
  28. {
  29. UPDATE_NONE,
  30. UPDATE_PENDING,
  31. UPDATE_BUSY
  32. };
  33. struct console_window
  34. {
  35. HDC mem_dc; /* memory DC holding the bitmap below */
  36. HBITMAP bitmap; /* bitmap of display window content */
  37. HFONT font; /* font used for rendering, usually fixed */
  38. HMENU popup_menu; /* popup menu triggered by right mouse click */
  39. HBITMAP cursor_bitmap; /* bitmap used for the caret */
  40. BOOL in_selection; /* an area is being selected */
  41. COORD selection_start; /* selection coordinates */
  42. COORD selection_end;
  43. unsigned int ui_charset; /* default UI charset */
  44. WCHAR *config_key; /* config registry key name */
  45. LONG ext_leading; /* external leading for font */
  46. BOOL quick_edit; /* whether mouse ops are sent to app or used for content selection */
  47. unsigned int menu_mask; /* MK_CONTROL MK_SHIFT mask to drive submenu opening */
  48. COORD win_pos; /* position (in cells) of visible part of screen buffer in window */
  49. unsigned int win_width; /* size (in cells) of visible part of window (width & height) */
  50. unsigned int win_height;
  51. unsigned int cursor_size; /* in % of cell height */
  52. int cursor_visible; /* cursor visibility */
  53. unsigned int sb_width; /* active screen buffer width */
  54. unsigned int sb_height; /* active screen buffer height */
  55. COORD cursor_pos; /* cursor position */
  56. RECT update; /* screen buffer update rect */
  57. enum update_state update_state; /* update state */
  58. };
  59. struct console_config
  60. {
  61. DWORD color_map[16]; /* console color table */
  62. unsigned int cell_width; /* width in pixels of a character */
  63. unsigned int cell_height; /* height in pixels of a character */
  64. unsigned int cursor_size; /* in % of cell height */
  65. int cursor_visible; /* cursor visibility */
  66. unsigned int attr; /* default fill attributes (screen colors) */
  67. unsigned int popup_attr ; /* pop-up color attributes */
  68. unsigned int history_size; /* number of commands in history buffer */
  69. unsigned int history_mode; /* flag if commands are not stored twice in buffer */
  70. unsigned int insert_mode; /* TRUE to insert text at the cursor location; FALSE to overwrite it */
  71. unsigned int menu_mask; /* MK_CONTROL MK_SHIFT mask to drive submenu opening */
  72. unsigned int quick_edit; /* whether mouse ops are sent to app or used for content selection */
  73. unsigned int sb_width; /* active screen buffer width */
  74. unsigned int sb_height; /* active screen buffer height */
  75. unsigned int win_width; /* size (in cells) of visible part of window (width & height) */
  76. unsigned int win_height;
  77. COORD win_pos; /* position (in cells) of visible part of screen buffer in window */
  78. unsigned int edition_mode; /* edition mode flavor while line editing */
  79. unsigned int font_pitch_family;
  80. unsigned int font_weight;
  81. WCHAR face_name[LF_FACESIZE];
  82. };
  83. static const char *debugstr_config( const struct console_config *config )
  84. {
  85. return wine_dbg_sprintf( "cell=(%u,%u) cursor=(%d,%d) attr=%02x pop-up=%02x font=%s/%u/%u "
  86. "hist=%u/%d flags=%c%c msk=%08x sb=(%u,%u) win=(%u,%u)x(%u,%u) edit=%u",
  87. config->cell_width, config->cell_height, config->cursor_size,
  88. config->cursor_visible, config->attr, config->popup_attr,
  89. wine_dbgstr_w(config->face_name), config->font_pitch_family,
  90. config->font_weight, config->history_size,
  91. config->history_mode ? 1 : 2,
  92. config->insert_mode ? 'I' : 'i',
  93. config->quick_edit ? 'Q' : 'q',
  94. config->menu_mask, config->sb_width, config->sb_height,
  95. config->win_pos.X, config->win_pos.Y, config->win_width,
  96. config->win_height, config->edition_mode );
  97. }
  98. static const char *debugstr_logfont( const LOGFONTW *lf, unsigned int ft )
  99. {
  100. return wine_dbg_sprintf( "%s%s%s%s lfHeight=%d lfWidth=%d lfEscapement=%d "
  101. "lfOrientation=%d lfWeight=%d lfItalic=%u lfUnderline=%u "
  102. "lfStrikeOut=%u lfCharSet=%u lfPitchAndFamily=%u lfFaceName=%s",
  103. (ft & RASTER_FONTTYPE) ? "raster" : "",
  104. (ft & TRUETYPE_FONTTYPE) ? "truetype" : "",
  105. ((ft & (RASTER_FONTTYPE|TRUETYPE_FONTTYPE)) == 0) ? "vector" : "",
  106. (ft & DEVICE_FONTTYPE) ? "|device" : "",
  107. lf->lfHeight, lf->lfWidth, lf->lfEscapement, lf->lfOrientation,
  108. lf->lfWeight, lf->lfItalic, lf->lfUnderline, lf->lfStrikeOut,
  109. lf->lfCharSet, lf->lfPitchAndFamily, wine_dbgstr_w( lf->lfFaceName ));
  110. }
  111. static const char *debugstr_textmetric( const TEXTMETRICW *tm, unsigned int ft )
  112. {
  113. return wine_dbg_sprintf( "%s%s%s%s tmHeight=%d tmAscent=%d tmDescent=%d "
  114. "tmAveCharWidth=%d tmMaxCharWidth=%d tmWeight=%d "
  115. "tmPitchAndFamily=%u tmCharSet=%u",
  116. (ft & RASTER_FONTTYPE) ? "raster" : "",
  117. (ft & TRUETYPE_FONTTYPE) ? "truetype" : "",
  118. ((ft & (RASTER_FONTTYPE|TRUETYPE_FONTTYPE)) == 0) ? "vector" : "",
  119. (ft & DEVICE_FONTTYPE) ? "|device" : "",
  120. tm->tmHeight, tm->tmAscent, tm->tmDescent, tm->tmAveCharWidth,
  121. tm->tmMaxCharWidth, tm->tmWeight, tm->tmPitchAndFamily,
  122. tm->tmCharSet );
  123. }
  124. /* read the basic configuration from any console key or subkey */
  125. static void load_registry_key( HKEY key, struct console_config *config )
  126. {
  127. DWORD type, count, val, i;
  128. WCHAR color_name[13];
  129. for (i = 0; i < ARRAY_SIZE(config->color_map); i++)
  130. {
  131. wsprintfW( color_name, L"ColorTable%02d", i );
  132. count = sizeof(val);
  133. if (!RegQueryValueExW( key, color_name, 0, &type, (BYTE *)&val, &count ))
  134. config->color_map[i] = val;
  135. }
  136. count = sizeof(val);
  137. if (!RegQueryValueExW( key, L"CursorSize", 0, &type, (BYTE *)&val, &count ))
  138. config->cursor_size = val;
  139. count = sizeof(val);
  140. if (!RegQueryValueExW( key, L"CursorVisible", 0, &type, (BYTE *)&val, &count ))
  141. config->cursor_visible = val;
  142. count = sizeof(val);
  143. if (!RegQueryValueExW( key, L"EditionMode", 0, &type, (BYTE *)&val, &count ))
  144. config->edition_mode = val;
  145. count = sizeof(config->face_name);
  146. RegQueryValueExW( key, L"FaceName", 0, &type, (BYTE *)&config->face_name, &count );
  147. count = sizeof(val);
  148. if (!RegQueryValueExW( key, L"FontPitchFamily", 0, &type, (BYTE *)&val, &count ))
  149. config->font_pitch_family = val;
  150. count = sizeof(val);
  151. if (!RegQueryValueExW( key, L"FontSize", 0, &type, (BYTE *)&val, &count ))
  152. {
  153. int height = HIWORD(val);
  154. int width = LOWORD(val);
  155. /* A value of zero reflects the default settings */
  156. if (height) config->cell_height = MulDiv( height, GetDpiForSystem(), USER_DEFAULT_SCREEN_DPI );
  157. if (width) config->cell_width = MulDiv( width, GetDpiForSystem(), USER_DEFAULT_SCREEN_DPI );
  158. }
  159. count = sizeof(val);
  160. if (!RegQueryValueExW( key, L"FontWeight", 0, &type, (BYTE *)&val, &count ))
  161. config->font_weight = val;
  162. count = sizeof(val);
  163. if (!RegQueryValueExW( key, L"HistoryBufferSize", 0, &type, (BYTE *)&val, &count ))
  164. config->history_size = val;
  165. count = sizeof(val);
  166. if (!RegQueryValueExW( key, L"HistoryNoDup", 0, &type, (BYTE *)&val, &count ))
  167. config->history_mode = val;
  168. count = sizeof(val);
  169. if (!RegQueryValueExW( key, L"wszInsertMode", 0, &type, (BYTE *)&val, &count ))
  170. config->insert_mode = val;
  171. count = sizeof(val);
  172. if (!RegQueryValueExW( key, L"MenuMask", 0, &type, (BYTE *)&val, &count ))
  173. config->menu_mask = val;
  174. count = sizeof(val);
  175. if (!RegQueryValueExW( key, L"PopupColors", 0, &type, (BYTE *)&val, &count ))
  176. config->popup_attr = val;
  177. count = sizeof(val);
  178. if (!RegQueryValueExW( key, L"QuickEdit", 0, &type, (BYTE *)&val, &count ))
  179. config->quick_edit = val;
  180. count = sizeof(val);
  181. if (!RegQueryValueExW( key, L"ScreenBufferSize", 0, &type, (BYTE *)&val, &count ))
  182. {
  183. config->sb_height = HIWORD(val);
  184. config->sb_width = LOWORD(val);
  185. }
  186. count = sizeof(val);
  187. if (!RegQueryValueExW( key, L"ScreenColors", 0, &type, (BYTE *)&val, &count ))
  188. config->attr = val;
  189. count = sizeof(val);
  190. if (!RegQueryValueExW( key, L"WindowSize", 0, &type, (BYTE *)&val, &count ))
  191. {
  192. config->win_height = HIWORD(val);
  193. config->win_width = LOWORD(val);
  194. }
  195. }
  196. /* load config from registry */
  197. static void load_config( const WCHAR *key_name, struct console_config *config )
  198. {
  199. static const COLORREF color_map[] =
  200. {
  201. RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x80), RGB(0x00, 0x80, 0x00), RGB(0x00, 0x80, 0x80),
  202. RGB(0x80, 0x00, 0x00), RGB(0x80, 0x00, 0x80), RGB(0x80, 0x80, 0x00), RGB(0xC0, 0xC0, 0xC0),
  203. RGB(0x80, 0x80, 0x80), RGB(0x00, 0x00, 0xFF), RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0xFF),
  204. RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0xFF, 0x00), RGB(0xFF, 0xFF, 0xFF)
  205. };
  206. HKEY key, app_key;
  207. TRACE("loading %s registry settings.\n", wine_dbgstr_w( key_name ));
  208. memcpy( config->color_map, color_map, sizeof(color_map) );
  209. memset( config->face_name, 0, sizeof(config->face_name) );
  210. config->cursor_size = 25;
  211. config->cursor_visible = 1;
  212. config->font_pitch_family = FIXED_PITCH | FF_DONTCARE;
  213. config->cell_height = MulDiv( 16, GetDpiForSystem(), USER_DEFAULT_SCREEN_DPI );
  214. config->cell_width = MulDiv( 8, GetDpiForSystem(), USER_DEFAULT_SCREEN_DPI );
  215. config->font_weight = FW_NORMAL;
  216. config->history_size = 50;
  217. config->history_mode = 0;
  218. config->insert_mode = 1;
  219. config->menu_mask = 0;
  220. config->popup_attr = 0xF5;
  221. config->quick_edit = 0;
  222. config->sb_height = 25;
  223. config->sb_width = 80;
  224. config->attr = 0x000F;
  225. config->win_height = 25;
  226. config->win_width = 80;
  227. config->win_pos.X = 0;
  228. config->win_pos.Y = 0;
  229. config->edition_mode = 0;
  230. /* read global settings */
  231. if (!RegOpenKeyW( HKEY_CURRENT_USER, L"Console", &key ))
  232. {
  233. load_registry_key( key, config );
  234. /* if requested, load part related to console title */
  235. if (key_name && !RegOpenKeyW( key, key_name, &app_key ))
  236. {
  237. load_registry_key( app_key, config );
  238. RegCloseKey( app_key );
  239. }
  240. RegCloseKey( key );
  241. }
  242. TRACE( "%s\n", debugstr_config( config ));
  243. }
  244. static void save_registry_key( HKEY key, const struct console_config *config )
  245. {
  246. DWORD val, width, height, i;
  247. WCHAR color_name[13];
  248. TRACE( "%s\n", debugstr_config( config ));
  249. for (i = 0; i < ARRAY_SIZE(config->color_map); i++)
  250. {
  251. wsprintfW( color_name, L"ColorTable%02d", i );
  252. val = config->color_map[i];
  253. RegSetValueExW( key, color_name, 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  254. }
  255. val = config->cursor_size;
  256. RegSetValueExW( key, L"CursorSize", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  257. val = config->cursor_visible;
  258. RegSetValueExW( key, L"CursorVisible", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  259. val = config->edition_mode;
  260. RegSetValueExW( key, L"EditionMode", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  261. RegSetValueExW( key, L"FaceName", 0, REG_SZ, (BYTE *)&config->face_name, sizeof(config->face_name) );
  262. val = config->font_pitch_family;
  263. RegSetValueExW( key, L"FontPitchFamily", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  264. width = MulDiv( config->cell_width, USER_DEFAULT_SCREEN_DPI, GetDpiForSystem() );
  265. height = MulDiv( config->cell_height, USER_DEFAULT_SCREEN_DPI, GetDpiForSystem() );
  266. val = MAKELONG( width, height );
  267. RegSetValueExW( key, L"FontSize", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  268. val = config->font_weight;
  269. RegSetValueExW( key, L"FontWeight", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  270. val = config->history_size;
  271. RegSetValueExW( key, L"HistoryBufferSize", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  272. val = config->history_mode;
  273. RegSetValueExW( key, L"HistoryNoDup", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  274. val = config->insert_mode;
  275. RegSetValueExW( key, L"InsertMode", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  276. val = config->menu_mask;
  277. RegSetValueExW( key, L"MenuMask", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  278. val = config->popup_attr;
  279. RegSetValueExW( key, L"PopupColors", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  280. val = config->quick_edit;
  281. RegSetValueExW( key, L"QuickEdit", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  282. val = MAKELONG(config->sb_width, config->sb_height);
  283. RegSetValueExW( key, L"ScreenBufferSize", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  284. val = config->attr;
  285. RegSetValueExW( key, L"ScreenColors", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  286. val = MAKELONG( config->win_width, config->win_height );
  287. RegSetValueExW( key, L"WindowSize", 0, REG_DWORD, (BYTE *)&val, sizeof(val) );
  288. }
  289. static void save_config( const WCHAR *key_name, const struct console_config *config )
  290. {
  291. HKEY key, app_key;
  292. TRACE( "%s %s\n", debugstr_w( key_name ), debugstr_config( config ));
  293. if (RegCreateKeyW( HKEY_CURRENT_USER, L"Console", &key ))
  294. {
  295. ERR("Can't open registry for saving\n");
  296. return;
  297. }
  298. if (key_name)
  299. {
  300. if (RegCreateKeyW( key, key_name, &app_key ))
  301. {
  302. ERR("Can't open registry for saving\n");
  303. }
  304. else
  305. {
  306. /* FIXME: maybe only save the values different from the default value ? */
  307. save_registry_key( app_key, config );
  308. RegCloseKey( app_key );
  309. }
  310. }
  311. else save_registry_key( key, config );
  312. RegCloseKey(key);
  313. }
  314. /* fill memory DC with current cells values */
  315. static void fill_mem_dc( struct console *console, const RECT *update )
  316. {
  317. unsigned int i, j, k;
  318. unsigned int attr;
  319. char_info_t *cell;
  320. HFONT old_font;
  321. HBRUSH brush;
  322. WCHAR *line;
  323. INT *dx;
  324. RECT r;
  325. if (!console->window->font || !console->window->bitmap)
  326. return;
  327. if (!(line = malloc( (update->right - update->left + 1) * sizeof(WCHAR))) ) return;
  328. dx = malloc( (update->right - update->left + 1) * sizeof(*dx) );
  329. old_font = SelectObject( console->window->mem_dc, console->window->font );
  330. for (j = update->top; j <= update->bottom; j++)
  331. {
  332. cell = &console->active->data[j * console->active->width];
  333. for (i = update->left; i <= update->right; i++)
  334. {
  335. attr = cell[i].attr;
  336. SetBkColor( console->window->mem_dc, console->active->color_map[(attr >> 4) & 0x0F] );
  337. SetTextColor( console->window->mem_dc, console->active->color_map[attr & 0x0F] );
  338. for (k = i; k <= update->right && cell[k].attr == attr; k++)
  339. {
  340. line[k - i] = cell[k].ch;
  341. dx[k - i] = console->active->font.width;
  342. }
  343. ExtTextOutW( console->window->mem_dc, i * console->active->font.width,
  344. j * console->active->font.height, 0, NULL, line, k - i, dx );
  345. if (console->window->ext_leading &&
  346. (brush = CreateSolidBrush( console->active->color_map[(attr >> 4) & 0x0F] )))
  347. {
  348. r.left = i * console->active->font.width;
  349. r.top = (j + 1) * console->active->font.height - console->window->ext_leading;
  350. r.right = k * console->active->font.width;
  351. r.bottom = (j + 1) * console->active->font.height;
  352. FillRect( console->window->mem_dc, &r, brush );
  353. DeleteObject( brush );
  354. }
  355. i = k - 1;
  356. }
  357. }
  358. SelectObject( console->window->mem_dc, old_font );
  359. free( dx );
  360. free( line );
  361. }
  362. /* set a new position for the cursor */
  363. static void update_window_cursor( struct console *console )
  364. {
  365. if (console->win != GetFocus() || !console->active->cursor_visible) return;
  366. SetCaretPos( (get_bounded_cursor_x( console->active ) - console->active->win.left) * console->active->font.width,
  367. (console->active->cursor_y - console->active->win.top) * console->active->font.height );
  368. ShowCaret( console->win );
  369. }
  370. /* sets a new shape for the cursor */
  371. static void shape_cursor( struct console *console )
  372. {
  373. int size = console->active->cursor_size;
  374. if (console->active->cursor_visible && console->win == GetFocus()) DestroyCaret();
  375. if (console->window->cursor_bitmap) DeleteObject( console->window->cursor_bitmap );
  376. console->window->cursor_bitmap = NULL;
  377. console->window->cursor_visible = FALSE;
  378. if (size != 100)
  379. {
  380. int w16b; /* number of bytes per row, aligned on word size */
  381. int i, j, nbl;
  382. BYTE *ptr;
  383. w16b = ((console->active->font.width + 15) & ~15) / 8;
  384. ptr = calloc( w16b, console->active->font.height );
  385. if (!ptr) return;
  386. nbl = max( (console->active->font.height * size) / 100, 1 );
  387. for (j = console->active->font.height - nbl; j < console->active->font.height; j++)
  388. {
  389. for (i = 0; i < console->active->font.width; i++)
  390. {
  391. ptr[w16b * j + (i / 8)] |= 0x80 >> (i & 7);
  392. }
  393. }
  394. console->window->cursor_bitmap = CreateBitmap( console->active->font.width,
  395. console->active->font.height, 1, 1, ptr );
  396. free(ptr);
  397. }
  398. }
  399. static void update_window( struct console *console )
  400. {
  401. unsigned int win_width, win_height;
  402. BOOL update_all = FALSE;
  403. int dx, dy;
  404. RECT r;
  405. console->window->update_state = UPDATE_BUSY;
  406. if (console->window->sb_width != console->active->width ||
  407. console->window->sb_height != console->active->height ||
  408. (!console->window->bitmap && IsWindowVisible( console->win )))
  409. {
  410. console->window->sb_width = console->active->width;
  411. console->window->sb_height = console->active->height;
  412. if (console->active->width && console->active->height && console->window->font)
  413. {
  414. HBITMAP bitmap;
  415. HDC dc;
  416. RECT r;
  417. if (!(dc = GetDC( console->win ))) return;
  418. bitmap = CreateCompatibleBitmap( dc,
  419. console->active->width * console->active->font.width,
  420. console->active->height * console->active->font.height );
  421. ReleaseDC( console->win, dc );
  422. SelectObject( console->window->mem_dc, bitmap );
  423. if (console->window->bitmap) DeleteObject( console->window->bitmap );
  424. console->window->bitmap = bitmap;
  425. SetRect( &r, 0, 0, console->active->width - 1, console->active->height - 1 );
  426. fill_mem_dc( console, &r );
  427. }
  428. empty_update_rect( console->active, &console->window->update );
  429. update_all = TRUE;
  430. }
  431. /* compute window size from desired client size */
  432. win_width = console->active->win.right - console->active->win.left + 1;
  433. win_height = console->active->win.bottom - console->active->win.top + 1;
  434. if (update_all || win_width != console->window->win_width ||
  435. win_height != console->window->win_height)
  436. {
  437. console->window->win_width = win_width;
  438. console->window->win_height = win_height;
  439. r.left = r.top = 0;
  440. r.right = win_width * console->active->font.width;
  441. r.bottom = win_height * console->active->font.height;
  442. AdjustWindowRect( &r, GetWindowLongW( console->win, GWL_STYLE ), FALSE );
  443. dx = dy = 0;
  444. if (console->active->width > win_width)
  445. {
  446. dy = GetSystemMetrics( SM_CYHSCROLL );
  447. SetScrollRange( console->win, SB_HORZ, 0, console->active->width - win_width, FALSE );
  448. SetScrollPos( console->win, SB_VERT, console->active->win.top, FALSE );
  449. ShowScrollBar( console->win, SB_HORZ, TRUE );
  450. }
  451. else
  452. {
  453. ShowScrollBar( console->win, SB_HORZ, FALSE );
  454. }
  455. if (console->active->height > win_height)
  456. {
  457. dx = GetSystemMetrics( SM_CXVSCROLL );
  458. SetScrollRange( console->win, SB_VERT, 0, console->active->height - win_height, FALSE );
  459. SetScrollPos( console->win, SB_VERT, console->active->win.top, FALSE );
  460. ShowScrollBar( console->win, SB_VERT, TRUE );
  461. }
  462. else
  463. ShowScrollBar( console->win, SB_VERT, FALSE );
  464. dx += r.right - r.left;
  465. dy += r.bottom - r.top;
  466. SetWindowPos( console->win, 0, 0, 0, dx, dy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
  467. SystemParametersInfoW( SPI_GETWORKAREA, 0, &r, 0 );
  468. console->active->max_width = (r.right - r.left) / console->active->font.width;
  469. console->active->max_height = (r.bottom - r.top - GetSystemMetrics( SM_CYCAPTION )) /
  470. console->active->font.height;
  471. InvalidateRect( console->win, NULL, FALSE );
  472. UpdateWindow( console->win );
  473. update_all = TRUE;
  474. }
  475. else if (console->active->win.left != console->window->win_pos.X ||
  476. console->active->win.top != console->window->win_pos.Y)
  477. {
  478. ScrollWindow( console->win,
  479. (console->window->win_pos.X - console->active->win.left) * console->active->font.width,
  480. (console->window->win_pos.Y - console->active->win.top) * console->active->font.height,
  481. NULL, NULL );
  482. SetScrollPos( console->win, SB_HORZ, console->active->win.left, TRUE );
  483. SetScrollPos( console->win, SB_VERT, console->active->win.top, TRUE );
  484. InvalidateRect( console->win, NULL, FALSE );
  485. }
  486. console->window->win_pos.X = console->active->win.left;
  487. console->window->win_pos.Y = console->active->win.top;
  488. if (console->window->update.top <= console->window->update.bottom &&
  489. console->window->update.left <= console->window->update.right)
  490. {
  491. RECT *update = &console->window->update;
  492. r.left = (update->left - console->active->win.left) * console->active->font.width;
  493. r.right = (update->right - console->active->win.left + 1) * console->active->font.width;
  494. r.top = (update->top - console->active->win.top) * console->active->font.height;
  495. r.bottom = (update->bottom - console->active->win.top + 1) * console->active->font.height;
  496. fill_mem_dc( console, update );
  497. empty_update_rect( console->active, &console->window->update );
  498. InvalidateRect( console->win, &r, FALSE );
  499. UpdateWindow( console->win );
  500. }
  501. if (update_all || console->active->cursor_size != console->window->cursor_size)
  502. {
  503. console->window->cursor_size = console->active->cursor_size;
  504. shape_cursor( console );
  505. }
  506. if (console->active->cursor_visible != console->window->cursor_visible)
  507. {
  508. console->window->cursor_visible = console->active->cursor_visible;
  509. if (console->win == GetFocus())
  510. {
  511. if (console->window->cursor_visible)
  512. CreateCaret( console->win, console->window->cursor_bitmap,
  513. console->active->font.width, console->active->font.height );
  514. else
  515. DestroyCaret();
  516. }
  517. }
  518. if (update_all || get_bounded_cursor_x( console->active ) != console->window->cursor_pos.X ||
  519. console->active->cursor_y != console->window->cursor_pos.Y)
  520. {
  521. console->window->cursor_pos.X = get_bounded_cursor_x( console->active );
  522. console->window->cursor_pos.Y = console->active->cursor_y;
  523. update_window_cursor( console );
  524. }
  525. console->window->update_state = UPDATE_NONE;
  526. }
  527. /* get the relevant information from the font described in lf and store them in config */
  528. static HFONT select_font_config( struct console_config *config, unsigned int cp, HWND hwnd,
  529. const LOGFONTW *lf )
  530. {
  531. HFONT font, old_font;
  532. TEXTMETRICW tm;
  533. CPINFO cpinfo;
  534. HDC dc;
  535. if (!(dc = GetDC( hwnd ))) return NULL;
  536. if (!(font = CreateFontIndirectW( lf )))
  537. {
  538. ReleaseDC( hwnd, dc );
  539. return NULL;
  540. }
  541. old_font = SelectObject( dc, font );
  542. GetTextMetricsW( dc, &tm );
  543. SelectObject( dc, old_font );
  544. ReleaseDC( hwnd, dc );
  545. config->cell_width = tm.tmAveCharWidth;
  546. config->cell_height = tm.tmHeight + tm.tmExternalLeading;
  547. config->font_weight = tm.tmWeight;
  548. lstrcpyW( config->face_name, lf->lfFaceName );
  549. /* FIXME: use maximum width for DBCS codepages since some chars take two cells */
  550. if (GetCPInfo( cp, &cpinfo ) && cpinfo.MaxCharSize > 1)
  551. config->cell_width = tm.tmMaxCharWidth;
  552. return font;
  553. }
  554. static void fill_logfont( LOGFONTW *lf, const WCHAR *name, unsigned int height, unsigned int weight )
  555. {
  556. lf->lfHeight = height;
  557. lf->lfWidth = 0;
  558. lf->lfEscapement = 0;
  559. lf->lfOrientation = 0;
  560. lf->lfWeight = weight;
  561. lf->lfItalic = FALSE;
  562. lf->lfUnderline = FALSE;
  563. lf->lfStrikeOut = FALSE;
  564. lf->lfCharSet = DEFAULT_CHARSET;
  565. lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
  566. lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
  567. lf->lfQuality = DEFAULT_QUALITY;
  568. lf->lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
  569. lstrcpyW( lf->lfFaceName, name );
  570. }
  571. static BOOL set_console_font( struct console *console, const LOGFONTW *logfont )
  572. {
  573. struct font_info *font_info = &console->active->font;
  574. HFONT font, old_font;
  575. TEXTMETRICW tm;
  576. CPINFO cpinfo;
  577. HDC dc;
  578. TRACE( "%s\n", debugstr_logfont( logfont, 0 ));
  579. if (console->window->font && logfont->lfHeight == console->active->font.height &&
  580. logfont->lfWeight == console->active->font.weight &&
  581. !logfont->lfItalic && !logfont->lfUnderline && !logfont->lfStrikeOut &&
  582. console->active->font.face_len == wcslen( logfont->lfFaceName ) * sizeof(WCHAR) &&
  583. !memcmp( logfont->lfFaceName, console->active->font.face_name,
  584. console->active->font.face_len ))
  585. {
  586. TRACE( "equal to current\n" );
  587. return TRUE;
  588. }
  589. if (!(dc = GetDC( console->win ))) return FALSE;
  590. if (!(font = CreateFontIndirectW( logfont )))
  591. {
  592. ReleaseDC( console->win, dc );
  593. return FALSE;
  594. }
  595. old_font = SelectObject( dc, font );
  596. GetTextMetricsW( dc, &tm );
  597. SelectObject( dc, old_font );
  598. ReleaseDC( console->win, dc );
  599. font_info->width = tm.tmAveCharWidth;
  600. font_info->height = tm.tmHeight + tm.tmExternalLeading;
  601. font_info->weight = tm.tmWeight;
  602. free( font_info->face_name );
  603. font_info->face_len = wcslen( logfont->lfFaceName ) * sizeof(WCHAR);
  604. font_info->face_name = malloc( font_info->face_len );
  605. memcpy( font_info->face_name, logfont->lfFaceName, font_info->face_len );
  606. /* FIXME: use maximum width for DBCS codepages since some chars take two cells */
  607. if (GetCPInfo( console->output_cp, &cpinfo ) && cpinfo.MaxCharSize > 1)
  608. font_info->width = tm.tmMaxCharWidth;
  609. if (console->window->font) DeleteObject( console->window->font );
  610. console->window->font = font;
  611. console->window->ext_leading = tm.tmExternalLeading;
  612. if (console->window->bitmap)
  613. {
  614. DeleteObject(console->window->bitmap);
  615. console->window->bitmap = NULL;
  616. }
  617. return TRUE;
  618. }
  619. struct font_chooser
  620. {
  621. struct console *console;
  622. int pass;
  623. BOOL done;
  624. };
  625. /* check if the font described in tm is usable as a font for the renderer */
  626. static BOOL validate_font_metric( struct console *console, const TEXTMETRICW *tm,
  627. DWORD type, int pass )
  628. {
  629. switch (pass) /* we get increasingly lenient in later passes */
  630. {
  631. case 0:
  632. if (type & RASTER_FONTTYPE) return FALSE;
  633. /* fall through */
  634. case 1:
  635. if (type & RASTER_FONTTYPE)
  636. {
  637. if (tm->tmMaxCharWidth * (console->active->win.right - console->active->win.left + 1)
  638. >= GetSystemMetrics(SM_CXSCREEN))
  639. return FALSE;
  640. if (tm->tmHeight * (console->active->win.bottom - console->active->win.top + 1)
  641. >= GetSystemMetrics(SM_CYSCREEN))
  642. return FALSE;
  643. }
  644. /* fall through */
  645. case 2:
  646. if (tm->tmCharSet != DEFAULT_CHARSET && tm->tmCharSet != console->window->ui_charset)
  647. return FALSE;
  648. /* fall through */
  649. case 3:
  650. if (tm->tmItalic || tm->tmUnderlined || tm->tmStruckOut) return FALSE;
  651. break;
  652. }
  653. return TRUE;
  654. }
  655. /* check if the font family described in lf is usable as a font for the renderer */
  656. static BOOL validate_font( struct console *console, const LOGFONTW *lf, int pass )
  657. {
  658. switch (pass) /* we get increasingly lenient in later passes */
  659. {
  660. case 0:
  661. case 1:
  662. case 2:
  663. if (lf->lfCharSet != DEFAULT_CHARSET && lf->lfCharSet != console->window->ui_charset)
  664. return FALSE;
  665. /* fall through */
  666. case 3:
  667. if ((lf->lfPitchAndFamily & 3) != FIXED_PITCH) return FALSE;
  668. /* fall through */
  669. case 4:
  670. if (lf->lfFaceName[0] == '@') return FALSE;
  671. break;
  672. }
  673. return TRUE;
  674. }
  675. /* helper functions to get a decent font for the renderer */
  676. static int WINAPI get_first_font_sub_enum( const LOGFONTW *lf, const TEXTMETRICW *tm,
  677. DWORD font_type, LPARAM lparam)
  678. {
  679. struct font_chooser *fc = (struct font_chooser *)lparam;
  680. TRACE( "%s\n", debugstr_textmetric( tm, font_type ));
  681. if (validate_font_metric( fc->console, tm, font_type, fc->pass ))
  682. {
  683. LOGFONTW mlf = *lf;
  684. /* Use the default sizes for the font (this is needed, especially for
  685. * TrueType fonts, so that we get a decent size, not the max size)
  686. */
  687. mlf.lfWidth = fc->console->active->font.width;
  688. mlf.lfHeight = fc->console->active->font.height;
  689. if (!mlf.lfHeight)
  690. mlf.lfHeight = MulDiv( 16, GetDpiForSystem(), USER_DEFAULT_SCREEN_DPI );
  691. if (set_console_font( fc->console, &mlf ))
  692. {
  693. struct console_config config;
  694. fc->done = 1;
  695. /* since we've modified the current config with new font information,
  696. * set this information as the new default.
  697. */
  698. load_config( fc->console->window->config_key, &config );
  699. config.cell_width = fc->console->active->font.width;
  700. config.cell_height = fc->console->active->font.height;
  701. fc->console->active->font.face_len = wcslen( config.face_name ) * sizeof(WCHAR);
  702. memcpy( fc->console->active->font.face_name, config.face_name,
  703. fc->console->active->font.face_len );
  704. /* Force also its writing back to the registry so that we can get it
  705. * the next time.
  706. */
  707. save_config( fc->console->window->config_key, &config );
  708. return 0;
  709. }
  710. }
  711. return 1;
  712. }
  713. static int WINAPI get_first_font_enum( const LOGFONTW *lf, const TEXTMETRICW *tm,
  714. DWORD font_type, LPARAM lparam )
  715. {
  716. struct font_chooser *fc = (struct font_chooser *)lparam;
  717. TRACE("%s\n", debugstr_logfont( lf, font_type ));
  718. if (validate_font( fc->console, lf, fc->pass ))
  719. {
  720. EnumFontFamiliesW( fc->console->window->mem_dc, lf->lfFaceName,
  721. get_first_font_sub_enum, lparam );
  722. return !fc->done; /* we just need the first matching one... */
  723. }
  724. return 1;
  725. }
  726. /* sets logfont as the new font for the console */
  727. static void update_console_font( struct console *console, const WCHAR *font,
  728. unsigned int height, unsigned int weight )
  729. {
  730. struct font_chooser fc;
  731. LOGFONTW lf;
  732. if (font[0] && height && weight)
  733. {
  734. fill_logfont( &lf, font, height, weight );
  735. if (set_console_font( console, &lf )) return;
  736. }
  737. /* try to find an acceptable font */
  738. WARN( "Couldn't match the font from registry, trying to find one\n" );
  739. fc.console = console;
  740. fc.done = FALSE;
  741. for (fc.pass = 0; fc.pass <= 5; fc.pass++)
  742. {
  743. EnumFontFamiliesW( console->window->mem_dc, NULL, get_first_font_enum, (LPARAM)&fc );
  744. if (fc.done) return;
  745. }
  746. ERR( "Couldn't find a decent font\n" );
  747. }
  748. /* get a cell from a relative coordinate in window (takes into account the scrolling) */
  749. static COORD get_cell( struct console *console, LPARAM lparam )
  750. {
  751. COORD c;
  752. c.X = console->active->win.left + (short)LOWORD(lparam) / console->active->font.width;
  753. c.Y = console->active->win.top + (short)HIWORD(lparam) / console->active->font.height;
  754. return c;
  755. }
  756. /* get the console bit mask equivalent to the VK_ status in key state */
  757. static DWORD get_ctrl_state( BYTE *key_state)
  758. {
  759. unsigned int ret = 0;
  760. GetKeyboardState(key_state);
  761. if (key_state[VK_SHIFT] & 0x80) ret |= SHIFT_PRESSED;
  762. if (key_state[VK_LCONTROL] & 0x80) ret |= LEFT_CTRL_PRESSED;
  763. if (key_state[VK_RCONTROL] & 0x80) ret |= RIGHT_CTRL_PRESSED;
  764. if (key_state[VK_LMENU] & 0x80) ret |= LEFT_ALT_PRESSED;
  765. if (key_state[VK_RMENU] & 0x80) ret |= RIGHT_ALT_PRESSED;
  766. if (key_state[VK_CAPITAL] & 0x01) ret |= CAPSLOCK_ON;
  767. if (key_state[VK_NUMLOCK] & 0x01) ret |= NUMLOCK_ON;
  768. if (key_state[VK_SCROLL] & 0x01) ret |= SCROLLLOCK_ON;
  769. return ret;
  770. }
  771. /* get the selection rectangle */
  772. static void get_selection_rect( struct console *console, RECT *r )
  773. {
  774. r->left = (min(console->window->selection_start.X, console->window->selection_end.X) -
  775. console->active->win.left) * console->active->font.width;
  776. r->top = (min(console->window->selection_start.Y, console->window->selection_end.Y) -
  777. console->active->win.top) * console->active->font.height;
  778. r->right = (max(console->window->selection_start.X, console->window->selection_end.X) + 1 -
  779. console->active->win.left) * console->active->font.width;
  780. r->bottom = (max(console->window->selection_start.Y, console->window->selection_end.Y) + 1 -
  781. console->active->win.top) * console->active->font.height;
  782. }
  783. static void update_selection( struct console *console, HDC ref_dc )
  784. {
  785. HDC dc;
  786. RECT r;
  787. get_selection_rect( console, &r );
  788. dc = ref_dc ? ref_dc : GetDC( console->win );
  789. if (!dc) return;
  790. if (console->win == GetFocus() && console->active->cursor_visible)
  791. HideCaret( console->win );
  792. InvertRect( dc, &r );
  793. if (dc != ref_dc)
  794. ReleaseDC( console->win, dc );
  795. if (console->win == GetFocus() && console->active->cursor_visible)
  796. ShowCaret( console->win );
  797. }
  798. static void move_selection( struct console *console, COORD c1, COORD c2 )
  799. {
  800. RECT r;
  801. HDC dc;
  802. if (c1.X < 0 || c1.X >= console->active->width ||
  803. c2.X < 0 || c2.X >= console->active->width ||
  804. c1.Y < 0 || c1.Y >= console->active->height ||
  805. c2.Y < 0 || c2.Y >= console->active->height)
  806. return;
  807. get_selection_rect( console, &r );
  808. dc = GetDC( console->win );
  809. if (dc)
  810. {
  811. if (console->win == GetFocus() && console->active->cursor_visible)
  812. HideCaret( console->win );
  813. InvertRect( dc, &r );
  814. }
  815. console->window->selection_start = c1;
  816. console->window->selection_end = c2;
  817. if (dc)
  818. {
  819. get_selection_rect( console, &r );
  820. InvertRect( dc, &r );
  821. ReleaseDC( console->win, dc );
  822. if (console->win == GetFocus() && console->active->cursor_visible)
  823. ShowCaret( console->win );
  824. }
  825. }
  826. /* copies the current selection into the clipboard */
  827. static void copy_selection( struct console *console )
  828. {
  829. unsigned int w, h;
  830. WCHAR *p, *buf;
  831. HANDLE mem;
  832. w = abs( console->window->selection_start.X - console->window->selection_end.X ) + 1;
  833. h = abs( console->window->selection_start.Y - console->window->selection_end.Y ) + 1;
  834. if (!OpenClipboard( console->win )) return;
  835. EmptyClipboard();
  836. mem = GlobalAlloc( GMEM_MOVEABLE, (w + 1) * h * sizeof(WCHAR) );
  837. if (mem && (p = buf = GlobalLock( mem )))
  838. {
  839. int x, y;
  840. COORD c;
  841. c.X = min( console->window->selection_start.X, console->window->selection_end.X );
  842. c.Y = min( console->window->selection_start.Y, console->window->selection_end.Y );
  843. for (y = c.Y; y < c.Y + h; y++)
  844. {
  845. WCHAR *end;
  846. for (x = c.X; x < c.X + w; x++)
  847. p[x - c.X] = console->active->data[y * console->active->width + x].ch;
  848. /* strip spaces from the end of the line */
  849. end = p + w;
  850. while (end > p && *(end - 1) == ' ')
  851. end--;
  852. *end = (y < c.Y + h - 1) ? '\n' : '\0';
  853. p = end + 1;
  854. }
  855. TRACE( "%s\n", debugstr_w( buf ));
  856. if (p - buf != (w + 1) * h)
  857. {
  858. HANDLE new_mem;
  859. new_mem = GlobalReAlloc( mem, (p - buf) * sizeof(WCHAR), GMEM_MOVEABLE );
  860. if (new_mem) mem = new_mem;
  861. }
  862. GlobalUnlock( mem );
  863. SetClipboardData( CF_UNICODETEXT, mem );
  864. }
  865. CloseClipboard();
  866. }
  867. static void paste_clipboard( struct console *console )
  868. {
  869. WCHAR *ptr;
  870. HANDLE h;
  871. if (!OpenClipboard( console->win )) return;
  872. h = GetClipboardData( CF_UNICODETEXT );
  873. if (h && (ptr = GlobalLock( h )))
  874. {
  875. unsigned int i, len = GlobalSize(h) / sizeof(WCHAR);
  876. INPUT_RECORD ir[2];
  877. SHORT sh;
  878. ir[0].EventType = KEY_EVENT;
  879. ir[0].Event.KeyEvent.wRepeatCount = 0;
  880. ir[0].Event.KeyEvent.dwControlKeyState = 0;
  881. ir[0].Event.KeyEvent.bKeyDown = TRUE;
  882. /* generate the corresponding input records */
  883. for (i = 0; i < len; i++)
  884. {
  885. /* FIXME: the modifying keys are not generated (shift, ctrl...) */
  886. sh = VkKeyScanW( ptr[i] );
  887. ir[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(sh);
  888. ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW( LOBYTE(sh), 0 );
  889. ir[0].Event.KeyEvent.uChar.UnicodeChar = ptr[i];
  890. ir[1] = ir[0];
  891. ir[1].Event.KeyEvent.bKeyDown = FALSE;
  892. write_console_input( console, ir, 2, i == len - 1 );
  893. }
  894. GlobalUnlock( h );
  895. }
  896. CloseClipboard();
  897. }
  898. /* handle keys while selecting an area */
  899. static void handle_selection_key( struct console *console, BOOL down, WPARAM wparam, LPARAM lparam )
  900. {
  901. BYTE key_state[256];
  902. COORD c1, c2;
  903. DWORD state;
  904. if (!down) return;
  905. state = get_ctrl_state( key_state ) & ~(CAPSLOCK_ON|NUMLOCK_ON|SCROLLLOCK_ON);
  906. switch (state)
  907. {
  908. case 0:
  909. switch (wparam)
  910. {
  911. case VK_RETURN:
  912. console->window->in_selection = FALSE;
  913. update_selection( console, 0 );
  914. copy_selection( console );
  915. return;
  916. case VK_RIGHT:
  917. c1 = console->window->selection_start;
  918. c2 = console->window->selection_end;
  919. c1.X++; c2.X++;
  920. move_selection( console, c1, c2 );
  921. return;
  922. case VK_LEFT:
  923. c1 = console->window->selection_start;
  924. c2 = console->window->selection_end;
  925. c1.X--; c2.X--;
  926. move_selection( console, c1, c2 );
  927. return;
  928. case VK_UP:
  929. c1 = console->window->selection_start;
  930. c2 = console->window->selection_end;
  931. c1.Y--; c2.Y--;
  932. move_selection( console, c1, c2 );
  933. return;
  934. case VK_DOWN:
  935. c1 = console->window->selection_start;
  936. c2 = console->window->selection_end;
  937. c1.Y++; c2.Y++;
  938. move_selection( console, c1, c2 );
  939. return;
  940. }
  941. break;
  942. case SHIFT_PRESSED:
  943. switch (wparam)
  944. {
  945. case VK_RIGHT:
  946. c1 = console->window->selection_start;
  947. c2 = console->window->selection_end;
  948. c2.X++;
  949. move_selection( console, c1, c2 );
  950. return;
  951. case VK_LEFT:
  952. c1 = console->window->selection_start;
  953. c2 = console->window->selection_end;
  954. c2.X--;
  955. move_selection( console, c1, c2 );
  956. return;
  957. case VK_UP:
  958. c1 = console->window->selection_start;
  959. c2 = console->window->selection_end;
  960. c2.Y--;
  961. move_selection( console, c1, c2 );
  962. return;
  963. case VK_DOWN:
  964. c1 = console->window->selection_start;
  965. c2 = console->window->selection_end;
  966. c2.Y++;
  967. move_selection( console, c1, c2 );
  968. return;
  969. }
  970. break;
  971. }
  972. if (wparam < VK_SPACE) /* Shift, Alt, Ctrl, Num Lock etc. */
  973. return;
  974. update_selection( console, 0 );
  975. console->window->in_selection = FALSE;
  976. }
  977. /* generate input_record from windows WM_KEYUP/WM_KEYDOWN messages */
  978. static void record_key_input( struct console *console, BOOL down, WPARAM wparam, LPARAM lparam )
  979. {
  980. static WCHAR last; /* keep last char seen as feed for key up message */
  981. BYTE key_state[256];
  982. INPUT_RECORD ir;
  983. WCHAR buf[2];
  984. ir.EventType = KEY_EVENT;
  985. ir.Event.KeyEvent.bKeyDown = down;
  986. ir.Event.KeyEvent.wRepeatCount = LOWORD(lparam);
  987. ir.Event.KeyEvent.wVirtualKeyCode = wparam;
  988. ir.Event.KeyEvent.wVirtualScanCode = HIWORD(lparam) & 0xFF;
  989. ir.Event.KeyEvent.uChar.UnicodeChar = 0;
  990. ir.Event.KeyEvent.dwControlKeyState = get_ctrl_state( key_state );
  991. if (lparam & (1u << 24)) ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY;
  992. if (down)
  993. {
  994. switch (ToUnicode(wparam, HIWORD(lparam), key_state, buf, 2, 0))
  995. {
  996. case 2:
  997. /* FIXME: should generate two events */
  998. /* fall through */
  999. case 1:
  1000. last = buf[0];
  1001. break;
  1002. default:
  1003. last = 0;
  1004. break;
  1005. }
  1006. }
  1007. ir.Event.KeyEvent.uChar.UnicodeChar = last;
  1008. if (!down) last = 0; /* FIXME: buggy HACK */
  1009. write_console_input( console, &ir, 1, TRUE );
  1010. }
  1011. static void record_mouse_input( struct console *console, COORD c, WPARAM wparam, DWORD event )
  1012. {
  1013. BYTE key_state[256];
  1014. INPUT_RECORD ir;
  1015. /* MOUSE_EVENTs shouldn't be sent unless ENABLE_MOUSE_INPUT is active */
  1016. if (!(console->mode & ENABLE_MOUSE_INPUT)) return;
  1017. ir.EventType = MOUSE_EVENT;
  1018. ir.Event.MouseEvent.dwMousePosition = c;
  1019. ir.Event.MouseEvent.dwButtonState = 0;
  1020. if (wparam & MK_LBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
  1021. if (wparam & MK_MBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
  1022. if (wparam & MK_RBUTTON) ir.Event.MouseEvent.dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
  1023. if (wparam & MK_CONTROL) ir.Event.MouseEvent.dwButtonState |= LEFT_CTRL_PRESSED;
  1024. if (wparam & MK_SHIFT) ir.Event.MouseEvent.dwButtonState |= SHIFT_PRESSED;
  1025. if (event == MOUSE_WHEELED) ir.Event.MouseEvent.dwButtonState |= wparam & 0xFFFF0000;
  1026. ir.Event.MouseEvent.dwControlKeyState = get_ctrl_state( key_state );
  1027. ir.Event.MouseEvent.dwEventFlags = event;
  1028. write_console_input( console, &ir, 1, TRUE );
  1029. }
  1030. struct dialog_info
  1031. {
  1032. struct console *console;
  1033. struct console_config config;
  1034. HWND dialog; /* handle to active propsheet */
  1035. int font_count; /* number of fonts */
  1036. struct dialog_font_info
  1037. {
  1038. unsigned int height;
  1039. unsigned int weight;
  1040. WCHAR faceName[LF_FACESIZE];
  1041. } *font; /* array of fonts */
  1042. };
  1043. /* dialog proc for the option property sheet */
  1044. static INT_PTR WINAPI option_dialog_proc( HWND dialog, UINT msg, WPARAM wparam, LPARAM lparam )
  1045. {
  1046. struct dialog_info *di;
  1047. unsigned int idc;
  1048. switch (msg)
  1049. {
  1050. case WM_INITDIALOG:
  1051. di = (struct dialog_info *)((PROPSHEETPAGEA *)lparam)->lParam;
  1052. di->dialog = dialog;
  1053. SetWindowLongPtrW( dialog, DWLP_USER, (LONG_PTR)di );
  1054. SendMessageW( GetDlgItem( dialog, IDC_OPT_HIST_SIZE_UD ), UDM_SETRANGE, 0, MAKELPARAM(500, 0) );
  1055. if (di->config.cursor_size <= 25) idc = IDC_OPT_CURSOR_SMALL;
  1056. else if (di->config.cursor_size <= 50) idc = IDC_OPT_CURSOR_MEDIUM;
  1057. else idc = IDC_OPT_CURSOR_LARGE;
  1058. SendDlgItemMessageW( dialog, idc, BM_SETCHECK, BST_CHECKED, 0 );
  1059. SetDlgItemInt( dialog, IDC_OPT_HIST_SIZE, di->config.history_size, FALSE );
  1060. SendDlgItemMessageW( dialog, IDC_OPT_HIST_NODOUBLE, BM_SETCHECK,
  1061. (di->config.history_mode) ? BST_CHECKED : BST_UNCHECKED, 0 );
  1062. SendDlgItemMessageW( dialog, IDC_OPT_INSERT_MODE, BM_SETCHECK,
  1063. (di->config.insert_mode) ? BST_CHECKED : BST_UNCHECKED, 0 );
  1064. SendDlgItemMessageW( dialog, IDC_OPT_CONF_CTRL, BM_SETCHECK,
  1065. (di->config.menu_mask & MK_CONTROL) ? BST_CHECKED : BST_UNCHECKED, 0 );
  1066. SendDlgItemMessageW( dialog, IDC_OPT_CONF_SHIFT, BM_SETCHECK,
  1067. (di->config.menu_mask & MK_SHIFT) ? BST_CHECKED : BST_UNCHECKED, 0 );
  1068. SendDlgItemMessageW( dialog, IDC_OPT_QUICK_EDIT, BM_SETCHECK,
  1069. (di->config.quick_edit) ? BST_CHECKED : BST_UNCHECKED, 0 );
  1070. return FALSE; /* because we set the focus */
  1071. case WM_COMMAND:
  1072. break;
  1073. case WM_NOTIFY:
  1074. {
  1075. NMHDR *nmhdr = (NMHDR*)lparam;
  1076. DWORD val;
  1077. BOOL done;
  1078. di = (struct dialog_info *)GetWindowLongPtrW( dialog, DWLP_USER );
  1079. switch (nmhdr->code)
  1080. {
  1081. case PSN_SETACTIVE:
  1082. /* needed in propsheet to keep properly the selected radio button
  1083. * otherwise, the focus would be set to the first tab stop in the
  1084. * propsheet, which would always activate the first radio button
  1085. */
  1086. if (IsDlgButtonChecked( dialog, IDC_OPT_CURSOR_SMALL ) == BST_CHECKED)
  1087. idc = IDC_OPT_CURSOR_SMALL;
  1088. else if (IsDlgButtonChecked( dialog, IDC_OPT_CURSOR_MEDIUM ) == BST_CHECKED)
  1089. idc = IDC_OPT_CURSOR_MEDIUM;
  1090. else
  1091. idc = IDC_OPT_CURSOR_LARGE;
  1092. PostMessageW( dialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem( dialog, idc ), TRUE );
  1093. di->dialog = dialog;
  1094. break;
  1095. case PSN_APPLY:
  1096. if (IsDlgButtonChecked( dialog, IDC_OPT_CURSOR_SMALL ) == BST_CHECKED) val = 25;
  1097. else if (IsDlgButtonChecked( dialog, IDC_OPT_CURSOR_MEDIUM ) == BST_CHECKED) val = 50;
  1098. else val = 100;
  1099. di->config.cursor_size = val;
  1100. val = GetDlgItemInt( dialog, IDC_OPT_HIST_SIZE, &done, FALSE );
  1101. if (done) di->config.history_size = val;
  1102. val = (IsDlgButtonChecked( dialog, IDC_OPT_HIST_NODOUBLE ) & BST_CHECKED) != 0;
  1103. di->config.history_mode = val;
  1104. val = (IsDlgButtonChecked( dialog, IDC_OPT_INSERT_MODE ) & BST_CHECKED) != 0;
  1105. di->config.insert_mode = val;
  1106. val = 0;
  1107. if (IsDlgButtonChecked( dialog, IDC_OPT_CONF_CTRL ) & BST_CHECKED) val |= MK_CONTROL;
  1108. if (IsDlgButtonChecked( dialog, IDC_OPT_CONF_SHIFT ) & BST_CHECKED) val |= MK_SHIFT;
  1109. di->config.menu_mask = val;
  1110. val = (IsDlgButtonChecked( dialog, IDC_OPT_QUICK_EDIT ) & BST_CHECKED) != 0;
  1111. di->config.quick_edit = val;
  1112. SetWindowLongPtrW( dialog, DWLP_MSGRESULT, PSNRET_NOERROR );
  1113. return TRUE;
  1114. default:
  1115. return FALSE;
  1116. }
  1117. break;
  1118. }
  1119. default:
  1120. return FALSE;
  1121. }
  1122. return TRUE;
  1123. }
  1124. static COLORREF get_color( struct dialog_info *di, unsigned int idc )
  1125. {
  1126. LONG_PTR index;
  1127. index = GetWindowLongPtrW(GetDlgItem( di->dialog, idc ), 0);
  1128. return di->config.color_map[index];
  1129. }
  1130. /* window proc for font previewer in font property sheet */
  1131. static LRESULT WINAPI font_preview_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
  1132. {
  1133. switch (msg)
  1134. {
  1135. case WM_CREATE:
  1136. SetWindowLongPtrW( hwnd, 0, 0 );
  1137. break;
  1138. case WM_GETFONT:
  1139. return GetWindowLongPtrW( hwnd, 0 );
  1140. case WM_SETFONT:
  1141. SetWindowLongPtrW( hwnd, 0, wparam );
  1142. if (LOWORD(lparam))
  1143. {
  1144. InvalidateRect( hwnd, NULL, TRUE );
  1145. UpdateWindow( hwnd );
  1146. }
  1147. break;
  1148. case WM_DESTROY:
  1149. {
  1150. HFONT font = (HFONT)GetWindowLongPtrW( hwnd, 0 );
  1151. if (font) DeleteObject( font );
  1152. break;
  1153. }
  1154. case WM_PAINT:
  1155. {
  1156. struct dialog_info *di;
  1157. HFONT font, old_font;
  1158. PAINTSTRUCT ps;
  1159. int size_idx;
  1160. di = (struct dialog_info *)GetWindowLongPtrW( GetParent( hwnd ), DWLP_USER );
  1161. BeginPaint( hwnd, &ps );
  1162. size_idx = SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0, 0 );
  1163. font = (HFONT)GetWindowLongPtrW( hwnd, 0 );
  1164. if (font)
  1165. {
  1166. static const WCHAR ascii[] = L"ASCII: abcXYZ";
  1167. COLORREF bkcolor;
  1168. WCHAR buf[256];
  1169. int len;
  1170. old_font = SelectObject( ps.hdc, font );
  1171. bkcolor = get_color( di, IDC_FNT_COLOR_BK );
  1172. FillRect( ps.hdc, &ps.rcPaint, CreateSolidBrush( bkcolor ));
  1173. SetBkColor( ps.hdc, bkcolor );
  1174. SetTextColor( ps.hdc, get_color( di, IDC_FNT_COLOR_FG ));
  1175. len = LoadStringW( GetModuleHandleW(NULL), IDS_FNT_PREVIEW, buf, ARRAY_SIZE(buf) );
  1176. if (len) TextOutW( ps.hdc, 0, 0, buf, len );
  1177. TextOutW( ps.hdc, 0, di->font[size_idx].height, ascii, ARRAY_SIZE(ascii) - 1 );
  1178. SelectObject( ps.hdc, old_font );
  1179. }
  1180. EndPaint( hwnd, &ps );
  1181. break;
  1182. }
  1183. default:
  1184. return DefWindowProcW( hwnd, msg, wparam, lparam );
  1185. }
  1186. return 0;
  1187. }
  1188. /* window proc for color previewer */
  1189. static LRESULT WINAPI color_preview_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
  1190. {
  1191. switch (msg)
  1192. {
  1193. case WM_PAINT:
  1194. {
  1195. struct dialog_info *di;
  1196. PAINTSTRUCT ps;
  1197. RECT client, r;
  1198. int i, step;
  1199. HBRUSH brush;
  1200. BeginPaint( hwnd, &ps );
  1201. GetClientRect( hwnd, &client );
  1202. step = client.right / 8;
  1203. di = (struct dialog_info *)GetWindowLongPtrW( GetParent(hwnd), DWLP_USER );
  1204. for (i = 0; i < 16; i++)
  1205. {
  1206. r.top = (i / 8) * (client.bottom / 2);
  1207. r.bottom = r.top + client.bottom / 2;
  1208. r.left = (i & 7) * step;
  1209. r.right = r.left + step;
  1210. brush = CreateSolidBrush( di->config.color_map[i] );
  1211. FillRect( ps.hdc, &r, brush );
  1212. DeleteObject( brush );
  1213. if (GetWindowLongW( hwnd, 0 ) == i)
  1214. {
  1215. HPEN old_pen;
  1216. int i = 2;
  1217. old_pen = SelectObject( ps.hdc, GetStockObject( WHITE_PEN ));
  1218. r.right--; r.bottom--;
  1219. for (;;)
  1220. {
  1221. MoveToEx( ps.hdc, r.left, r.bottom, NULL );
  1222. LineTo( ps.hdc, r.left, r.top );
  1223. LineTo( ps.hdc, r.right, r.top );
  1224. SelectObject( ps.hdc, GetStockObject( BLACK_PEN ));
  1225. LineTo( ps.hdc, r.right, r.bottom );
  1226. LineTo( ps.hdc, r.left, r.bottom );
  1227. if (--i == 0) break;
  1228. r.left++; r.top++; r.right--; r.bottom--;
  1229. SelectObject( ps.hdc, GetStockObject( WHITE_PEN ));
  1230. }
  1231. SelectObject( ps.hdc, old_pen );
  1232. }
  1233. }
  1234. EndPaint( hwnd, &ps );
  1235. break;
  1236. }
  1237. case WM_LBUTTONDOWN:
  1238. {
  1239. int i, step;
  1240. RECT client;
  1241. GetClientRect( hwnd, &client );
  1242. step = client.right / 8;
  1243. i = (HIWORD(lparam) >= client.bottom / 2) ? 8 : 0;
  1244. i += LOWORD(lparam) / step;
  1245. SetWindowLongW( hwnd, 0, i );
  1246. InvalidateRect( GetDlgItem( GetParent( hwnd ), IDC_FNT_PREVIEW ), NULL, FALSE );
  1247. InvalidateRect( hwnd, NULL, FALSE );
  1248. break;
  1249. }
  1250. default:
  1251. return DefWindowProcW( hwnd, msg, wparam, lparam );
  1252. }
  1253. return 0;
  1254. }
  1255. /* enumerates all the font names with at least one valid font */
  1256. static int WINAPI font_enum_size2( const LOGFONTW *lf, const TEXTMETRICW *tm,
  1257. DWORD font_type, LPARAM lparam )
  1258. {
  1259. struct dialog_info *di = (struct dialog_info *)lparam;
  1260. TRACE( "%s\n", debugstr_textmetric( tm, font_type ));
  1261. if (validate_font_metric( di->console, tm, font_type, 0 )) di->font_count++;
  1262. return 1;
  1263. }
  1264. static int WINAPI font_enum( const LOGFONTW *lf, const TEXTMETRICW *tm,
  1265. DWORD font_type, LPARAM lparam )
  1266. {
  1267. struct dialog_info *di = (struct dialog_info *)lparam;
  1268. TRACE( "%s\n", debugstr_logfont( lf, font_type ));
  1269. if (validate_font( di->console, lf, 0 ))
  1270. {
  1271. if (font_type & RASTER_FONTTYPE)
  1272. {
  1273. di->font_count = 0;
  1274. EnumFontFamiliesW( di->console->window->mem_dc, lf->lfFaceName,
  1275. font_enum_size2, (LPARAM)di );
  1276. }
  1277. else
  1278. di->font_count = 1;
  1279. if (di->font_count)
  1280. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_FONT, LB_ADDSTRING,
  1281. 0, (LPARAM)lf->lfFaceName );
  1282. }
  1283. return 1;
  1284. }
  1285. static int WINAPI font_enum_size( const LOGFONTW *lf, const TEXTMETRICW *tm,
  1286. DWORD font_type, LPARAM lparam )
  1287. {
  1288. struct dialog_info *di = (struct dialog_info *)lparam;
  1289. WCHAR buf[32];
  1290. TRACE( "%s\n", debugstr_textmetric( tm, font_type ));
  1291. if (di->font_count == 0 && !(font_type & RASTER_FONTTYPE))
  1292. {
  1293. static const int sizes[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
  1294. int i;
  1295. di->font_count = ARRAY_SIZE(sizes);
  1296. di->font = malloc( di->font_count * sizeof(di->font[0]) );
  1297. for (i = 0; i < di->font_count; i++)
  1298. {
  1299. /* drop sizes where window size wouldn't fit on screen */
  1300. if (sizes[i] * di->config.win_height > GetSystemMetrics( SM_CYSCREEN ))
  1301. {
  1302. di->font_count = i;
  1303. break;
  1304. }
  1305. di->font[i].height = sizes[i];
  1306. di->font[i].weight = 400;
  1307. lstrcpyW( di->font[i].faceName, lf->lfFaceName );
  1308. wsprintfW( buf, L"%d", sizes[i] );
  1309. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_SIZE, LB_INSERTSTRING, i, (LPARAM)buf );
  1310. }
  1311. /* don't need to enumerate other */
  1312. return 0;
  1313. }
  1314. if (validate_font_metric( di->console, tm, font_type, 0 ))
  1315. {
  1316. int idx = 0;
  1317. /* we want the string to be sorted with a numeric order, not a lexicographic...
  1318. * do the job by hand... get where to insert the new string
  1319. */
  1320. while (idx < di->font_count && tm->tmHeight > di->font[idx].height)
  1321. idx++;
  1322. while (idx < di->font_count &&
  1323. tm->tmHeight == di->font[idx].height &&
  1324. tm->tmWeight > di->font[idx].weight)
  1325. idx++;
  1326. if (idx == di->font_count ||
  1327. tm->tmHeight != di->font[idx].height ||
  1328. tm->tmWeight < di->font[idx].weight)
  1329. {
  1330. /* here we need to add the new entry */
  1331. wsprintfW( buf, L"%d", tm->tmHeight );
  1332. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_SIZE, LB_INSERTSTRING, idx, (LPARAM)buf );
  1333. /* now grow our arrays and insert the values at the same index than in the list box */
  1334. if (di->font_count)
  1335. {
  1336. di->font = realloc( di->font, sizeof(*di->font) * (di->font_count + 1) );
  1337. if (idx != di->font_count)
  1338. memmove( &di->font[idx + 1], &di->font[idx],
  1339. (di->font_count - idx) * sizeof(*di->font) );
  1340. }
  1341. else
  1342. di->font = malloc( sizeof(*di->font) );
  1343. di->font[idx].height = tm->tmHeight;
  1344. di->font[idx].weight = tm->tmWeight;
  1345. lstrcpyW( di->font[idx].faceName, lf->lfFaceName );
  1346. di->font_count++;
  1347. }
  1348. }
  1349. return 1;
  1350. }
  1351. static BOOL select_font( struct dialog_info *di )
  1352. {
  1353. struct console_config config;
  1354. int font_idx, size_idx;
  1355. HFONT font, old_font;
  1356. DWORD_PTR args[2];
  1357. WCHAR buf[256];
  1358. WCHAR fmt[128];
  1359. LOGFONTW lf;
  1360. font_idx = SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_FONT, LB_GETCURSEL, 0, 0 );
  1361. size_idx = SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0, 0 );
  1362. if (font_idx < 0 || size_idx < 0 || size_idx >= di->font_count)
  1363. return FALSE;
  1364. fill_logfont( &lf, di->font[size_idx].faceName, di->font[size_idx].height,
  1365. di->font[size_idx].weight );
  1366. font = select_font_config( &config, di->console->output_cp, di->console->win, &lf );
  1367. if (!font) return FALSE;
  1368. if (config.cell_height != di->font[size_idx].height)
  1369. TRACE( "mismatched heights (%u<>%u)\n", config.cell_height, di->font[size_idx].height );
  1370. old_font = (HFONT)SendDlgItemMessageW( di->dialog, IDC_FNT_PREVIEW, WM_GETFONT, 0, 0 );
  1371. SendDlgItemMessageW( di->dialog, IDC_FNT_PREVIEW, WM_SETFONT, (WPARAM)font, TRUE );
  1372. if (old_font) DeleteObject( old_font );
  1373. LoadStringW( GetModuleHandleW(NULL), IDS_FNT_DISPLAY, fmt, ARRAY_SIZE(fmt) );
  1374. args[0] = config.cell_width;
  1375. args[1] = config.cell_height;
  1376. FormatMessageW( FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1377. fmt, 0, 0, buf, ARRAY_SIZE(buf), (__ms_va_list*)args );
  1378. SendDlgItemMessageW( di->dialog, IDC_FNT_FONT_INFO, WM_SETTEXT, 0, (LPARAM)buf );
  1379. return TRUE;
  1380. }
  1381. /* fills the size list box according to selected family in font LB */
  1382. static BOOL fill_list_size( struct dialog_info *di, BOOL init )
  1383. {
  1384. WCHAR face_name[LF_FACESIZE];
  1385. int idx = 0;
  1386. idx = SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_FONT, LB_GETCURSEL, 0, 0 );
  1387. if (idx < 0) return FALSE;
  1388. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_FONT, LB_GETTEXT, idx, (LPARAM)face_name );
  1389. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_SIZE, LB_RESETCONTENT, 0, 0 );
  1390. free( di->font );
  1391. di->font_count = 0;
  1392. di->font = NULL;
  1393. EnumFontFamiliesW( di->console->window->mem_dc, face_name, font_enum_size, (LPARAM)di );
  1394. if (init)
  1395. {
  1396. int ref = -1;
  1397. for (idx = 0; idx < di->font_count; idx++)
  1398. {
  1399. if (!lstrcmpW( di->font[idx].faceName, di->config.face_name ) &&
  1400. di->font[idx].height == di->config.cell_height &&
  1401. di->font[idx].weight == di->config.font_weight)
  1402. {
  1403. if (ref == -1) ref = idx;
  1404. else TRACE("Several matches found: ref=%d idx=%d\n", ref, idx);
  1405. }
  1406. }
  1407. idx = (ref == -1) ? 0 : ref;
  1408. }
  1409. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_SIZE, LB_SETCURSEL, idx, 0 );
  1410. select_font( di );
  1411. return TRUE;
  1412. }
  1413. static BOOL fill_list_font( struct dialog_info *di )
  1414. {
  1415. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_FONT, LB_RESETCONTENT, 0, 0 );
  1416. EnumFontFamiliesW( di->console->window->mem_dc, NULL, font_enum, (LPARAM)di );
  1417. if (SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_FONT, LB_SELECTSTRING,
  1418. -1, (LPARAM)di->config.face_name ) == LB_ERR)
  1419. SendDlgItemMessageW( di->dialog, IDC_FNT_LIST_FONT, LB_SETCURSEL, 0, 0 );
  1420. fill_list_size( di, TRUE );
  1421. return TRUE;
  1422. }
  1423. /* dialog proc for the font property sheet */
  1424. static INT_PTR WINAPI font_dialog_proc( HWND dialog, UINT msg, WPARAM wparam, LPARAM lparam )
  1425. {
  1426. struct dialog_info *di;
  1427. switch (msg)
  1428. {
  1429. case WM_INITDIALOG:
  1430. di = (struct dialog_info *)((PROPSHEETPAGEA*)lparam)->lParam;
  1431. di->dialog = dialog;
  1432. SetWindowLongPtrW( dialog, DWLP_USER, (DWORD_PTR)di );
  1433. /* remove dialog from this control, font will be reset when listboxes are filled */
  1434. SendDlgItemMessageW( dialog, IDC_FNT_PREVIEW, WM_SETFONT, 0, 0 );
  1435. fill_list_font( di );
  1436. SetWindowLongW( GetDlgItem( dialog, IDC_FNT_COLOR_BK ), 0, (di->config.attr >> 4) & 0x0F );
  1437. SetWindowLongW( GetDlgItem( dialog, IDC_FNT_COLOR_FG ), 0, di->config.attr & 0x0F );
  1438. break;
  1439. case WM_COMMAND:
  1440. di = (struct dialog_info *)GetWindowLongPtrW( dialog, DWLP_USER );
  1441. switch (LOWORD(wparam))
  1442. {
  1443. case IDC_FNT_LIST_FONT:
  1444. if (HIWORD(wparam) == LBN_SELCHANGE)
  1445. fill_list_size( di, FALSE );
  1446. break;
  1447. case IDC_FNT_LIST_SIZE:
  1448. if (HIWORD(wparam) == LBN_SELCHANGE)
  1449. select_font( di );
  1450. break;
  1451. }
  1452. break;
  1453. case WM_NOTIFY:
  1454. {
  1455. NMHDR *nmhdr = (NMHDR*)lparam;
  1456. DWORD val;
  1457. di = (struct dialog_info*)GetWindowLongPtrW( dialog, DWLP_USER );
  1458. switch (nmhdr->code)
  1459. {
  1460. case PSN_SETACTIVE:
  1461. di->dialog = dialog;
  1462. break;
  1463. case PSN_APPLY:
  1464. val = SendDlgItemMessageW( dialog, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0, 0 );
  1465. if (val < di->font_count)
  1466. {
  1467. LOGFONTW lf;
  1468. fill_logfont( &lf, di->font[val].faceName, di->font[val].height, di->font[val].weight );
  1469. DeleteObject( select_font_config( &di->config, di->console->output_cp,
  1470. di->console->win, &lf ));
  1471. }
  1472. val = (GetWindowLongW( GetDlgItem( dialog, IDC_FNT_COLOR_BK ), 0 ) << 4) |
  1473. GetWindowLongW( GetDlgItem( dialog, IDC_FNT_COLOR_FG ), 0 );
  1474. di->config.attr = val;
  1475. SetWindowLongPtrW( dialog, DWLP_MSGRESULT, PSNRET_NOERROR );
  1476. return TRUE;
  1477. default:
  1478. return FALSE;
  1479. }
  1480. break;
  1481. }
  1482. default:
  1483. return FALSE;
  1484. }
  1485. return TRUE;
  1486. }
  1487. /* dialog proc for the config property sheet */
  1488. static INT_PTR WINAPI config_dialog_proc( HWND dialog, UINT msg, WPARAM wparam, LPARAM lparam )
  1489. {
  1490. struct dialog_info *di;
  1491. int max_ud = 2000;
  1492. switch (msg)
  1493. {
  1494. case WM_INITDIALOG:
  1495. di = (struct dialog_info *)((PROPSHEETPAGEA*)lparam)->lParam;
  1496. di->dialog = dialog;
  1497. SetWindowLongPtrW( dialog, DWLP_USER, (DWORD_PTR)di );
  1498. SetDlgItemInt( dialog, IDC_CNF_SB_WIDTH, di->config.sb_width, FALSE );
  1499. SetDlgItemInt( dialog, IDC_CNF_SB_HEIGHT, di->config.sb_height, FALSE );
  1500. SetDlgItemInt( dialog, IDC_CNF_WIN_WIDTH, di->config.win_width, FALSE );
  1501. SetDlgItemInt( dialog, IDC_CNF_WIN_HEIGHT, di->config.win_height, FALSE );
  1502. SendMessageW( GetDlgItem(dialog, IDC_CNF_WIN_HEIGHT_UD), UDM_SETRANGE, 0, MAKELPARAM(max_ud, 0));
  1503. SendMessageW( GetDlgItem(dialog, IDC_CNF_WIN_WIDTH_UD), UDM_SETRANGE, 0, MAKELPARAM(max_ud, 0));
  1504. SendMessageW( GetDlgItem(dialog, IDC_CNF_SB_HEIGHT_UD), UDM_SETRANGE, 0, MAKELPARAM(max_ud, 0));
  1505. SendMessageW( GetDlgItem(dialog, IDC_CNF_SB_WIDTH_UD), UDM_SETRANGE, 0, MAKELPARAM(max_ud, 0));
  1506. SendDlgItemMessageW( dialog, IDC_CNF_CLOSE_EXIT, BM_SETCHECK, BST_CHECKED, 0 );
  1507. SendDlgItemMessageW( dialog, IDC_CNF_EDITION_MODE, CB_ADDSTRING, 0, (LPARAM)L"Win32" );
  1508. SendDlgItemMessageW( dialog, IDC_CNF_EDITION_MODE, CB_ADDSTRING, 0, (LPARAM)L"Emacs" );
  1509. SendDlgItemMessageW( dialog, IDC_CNF_EDITION_MODE, CB_SETCURSEL, di->config.edition_mode, 0 );
  1510. break;
  1511. case WM_NOTIFY:
  1512. {
  1513. NMHDR *nmhdr = (NMHDR*)lparam;
  1514. int win_w, win_h, sb_w, sb_h;
  1515. BOOL st1, st2;
  1516. di = (struct dialog_info *)GetWindowLongPtrW( dialog, DWLP_USER );
  1517. switch (nmhdr->code)
  1518. {
  1519. case PSN_SETACTIVE:
  1520. di->dialog = dialog;
  1521. break;
  1522. case PSN_APPLY:
  1523. sb_w = GetDlgItemInt( dialog, IDC_CNF_SB_WIDTH, &st1, FALSE );
  1524. sb_h = GetDlgItemInt( dialog, IDC_CNF_SB_HEIGHT, &st2, FALSE );
  1525. if (!st1 || ! st2)
  1526. {
  1527. SetWindowLongPtrW( dialog, DWLP_MSGRESULT, PSNRET_INVALID );
  1528. return TRUE;
  1529. }
  1530. win_w = GetDlgItemInt( dialog, IDC_CNF_WIN_WIDTH, &st1, FALSE );
  1531. win_h = GetDlgItemInt( dialog, IDC_CNF_WIN_HEIGHT, &st2, FALSE );
  1532. if (!st1 || !st2)
  1533. {
  1534. SetWindowLongPtrW( dialog, DWLP_MSGRESULT, PSNRET_INVALID );
  1535. return TRUE;
  1536. }
  1537. if (win_w > sb_w || win_h > sb_h)
  1538. {
  1539. WCHAR cap[256];
  1540. WCHAR txt[256];
  1541. LoadStringW( GetModuleHandleW(NULL), IDS_DLG_TIT_ERROR, cap, ARRAY_SIZE(cap) );
  1542. LoadStringW( GetModuleHandleW(NULL), IDS_DLG_ERR_SBWINSIZE, txt, ARRAY_SIZE(txt) );
  1543. MessageBoxW( dialog, txt, cap, MB_OK );
  1544. SetWindowLongPtrW( dialog, DWLP_MSGRESULT, PSNRET_INVALID );
  1545. return TRUE;
  1546. }
  1547. di->config.win_width = win_w;
  1548. di->config.win_height = win_h;
  1549. di->config.sb_width = sb_w;
  1550. di->config.sb_height = sb_h;
  1551. di->config.edition_mode = SendDlgItemMessageW( dialog, IDC_CNF_EDITION_MODE,
  1552. CB_GETCURSEL, 0, 0 );
  1553. SetWindowLongPtrW( dialog, DWLP_MSGRESULT, PSNRET_NOERROR );
  1554. return TRUE;
  1555. default:
  1556. return FALSE;
  1557. }
  1558. break;
  1559. }
  1560. default:
  1561. return FALSE;
  1562. }
  1563. return TRUE;
  1564. }
  1565. /* dialog proc for choosing how to handle modification to the console settings */
  1566. static INT_PTR WINAPI save_dialog_proc( HWND dialog, UINT msg, WPARAM wparam, LPARAM lparam )
  1567. {
  1568. switch (msg)
  1569. {
  1570. case WM_INITDIALOG:
  1571. SendDlgItemMessageW( dialog, IDC_SAV_SESSION, BM_SETCHECK, BST_CHECKED, 0 );
  1572. break;
  1573. case WM_COMMAND:
  1574. switch (LOWORD(wparam))
  1575. {
  1576. case IDOK:
  1577. EndDialog( dialog,
  1578. (IsDlgButtonChecked(dialog, IDC_SAV_SAVE) == BST_CHECKED) ?
  1579. IDC_SAV_SAVE : IDC_SAV_SESSION );
  1580. break;
  1581. case IDCANCEL:
  1582. EndDialog( dialog, IDCANCEL ); break;
  1583. }
  1584. break;
  1585. default:
  1586. return FALSE;
  1587. }
  1588. return TRUE;
  1589. }
  1590. static void apply_config( struct console *console, const struct console_config *config )
  1591. {
  1592. if (console->active->width != config->sb_width || console->active->height != config->sb_height)
  1593. change_screen_buffer_size( console->active, config->sb_width, config->sb_height );
  1594. console->window->menu_mask = config->menu_mask;
  1595. console->window->quick_edit = config->quick_edit;
  1596. console->edition_mode = config->edition_mode;
  1597. console->history_mode = config->history_mode;
  1598. if (console->history_size != config->history_size)
  1599. {
  1600. struct history_line **mem = NULL;
  1601. int i, delta;
  1602. if (config->history_size && (mem = malloc( config->history_size * sizeof(*mem) )))
  1603. {
  1604. memset( mem, 0, config->history_size * sizeof(*mem) );
  1605. delta = (console->history_index > config->history_size)
  1606. ? (console->history_index - config->history_size) : 0;
  1607. for (i = delta; i < console->history_index; i++)
  1608. {
  1609. mem[i - delta] = console->history[i];
  1610. console->history[i] = NULL;
  1611. }
  1612. console->history_index -= delta;
  1613. for (i = 0; i < console->history_size; i++)
  1614. free( console->history[i] );
  1615. free( console->history );
  1616. console->history = mem;
  1617. console->history_size = config->history_size;
  1618. }
  1619. }
  1620. if (config->insert_mode)
  1621. console->mode |= ENABLE_INSERT_MODE|ENABLE_EXTENDED_FLAGS;
  1622. else
  1623. console->mode &= ~ENABLE_INSERT_MODE;
  1624. console->active->cursor_size = config->cursor_size;
  1625. console->active->cursor_visible = config->cursor_visible;
  1626. console->active->attr = config->attr;
  1627. console->active->popup_attr = config->popup_attr;
  1628. console->active->win.left = config->win_pos.X;
  1629. console->active->win.top = config->win_pos.Y;
  1630. console->active->win.right = config->win_pos.X + config->win_width - 1;
  1631. console->active->win.bottom = config->win_pos.Y + config->win_height - 1;
  1632. memcpy( console->active->color_map, config->color_map, sizeof(config->color_map) );
  1633. if (console->active->font.width != config->cell_width ||
  1634. console->active->font.height != config->cell_height ||
  1635. console->active->font.weight != config->font_weight ||
  1636. console->active->font.pitch_family != config->font_pitch_family ||
  1637. console->active->font.face_len != wcslen( config->face_name ) * sizeof(WCHAR) ||
  1638. memcmp( console->active->font.face_name, config->face_name, console->active->font.face_len ))
  1639. {
  1640. update_console_font( console, config->face_name, config->cell_height, config->font_weight );
  1641. }
  1642. update_window( console );
  1643. notify_screen_buffer_size( console->active );
  1644. }
  1645. static void current_config( struct console *console, struct console_config *config )
  1646. {
  1647. size_t len;
  1648. config->menu_mask = console->window->menu_mask;
  1649. config->quick_edit = console->window->quick_edit;
  1650. config->edition_mode = console->edition_mode;
  1651. config->history_mode = console->history_mode;
  1652. config->history_size = console->history_size;
  1653. config->insert_mode = (console->mode & (ENABLE_INSERT_MODE|ENABLE_EXTENDED_FLAGS)) ==
  1654. (ENABLE_INSERT_MODE|ENABLE_EXTENDED_FLAGS);
  1655. config->cursor_size = console->active->cursor_size;
  1656. config->cursor_visible = console->active->cursor_visible;
  1657. config->attr = console->active->attr;
  1658. config->popup_attr = console->active->popup_attr;
  1659. memcpy( config->color_map, console->active->color_map, sizeof(config->color_map) );
  1660. config->win_height = console->active->win.bottom - console->active->win.top + 1;
  1661. config->win_width = console->active->win.right - console->active->win.left + 1;
  1662. config->cell_width = console->active->font.width;
  1663. config->cell_height = console->active->font.height;
  1664. config->font_weight = console->active->font.weight;
  1665. config->font_pitch_family = console->active->font.pitch_family;
  1666. len = min( ARRAY_SIZE(config->face_name) - 1, console->active->font.face_len / sizeof(WCHAR) );
  1667. if (len) memcpy( config->face_name, console->active->font.face_name, len * sizeof(WCHAR) );
  1668. config->face_name[len] = 0;
  1669. config->sb_width = console->active->width;
  1670. config->sb_height = console->active->height;
  1671. config->win_width = console->active->win.right - console->active->win.left + 1;
  1672. config->win_height = console->active->win.bottom - console->active->win.top + 1;
  1673. config->win_pos.X = console->active->win.left;
  1674. config->win_pos.Y = console->active->win.top;
  1675. }
  1676. /* run the dialog box to set up the console options */
  1677. static BOOL config_dialog( struct console *console, BOOL current )
  1678. {
  1679. struct console_config prev_config;
  1680. struct dialog_info di;
  1681. PROPSHEETHEADERW header;
  1682. HPROPSHEETPAGE pages[3];
  1683. PROPSHEETPAGEW psp;
  1684. WNDCLASSW wndclass;
  1685. WCHAR buff[256];
  1686. BOOL modify_session = FALSE;
  1687. BOOL save = FALSE;
  1688. InitCommonControls();
  1689. memset( &di, 0, sizeof(di) );
  1690. di.console = console;
  1691. if (!current)
  1692. {
  1693. load_config( NULL, &di.config );
  1694. save = TRUE;
  1695. }
  1696. else current_config( console, &di.config );
  1697. prev_config = di.config;
  1698. di.font_count = 0;
  1699. di.font = NULL;
  1700. wndclass.style = 0;
  1701. wndclass.lpfnWndProc = font_preview_proc;
  1702. wndclass.cbClsExtra = 0;
  1703. wndclass.cbWndExtra = sizeof(HFONT);
  1704. wndclass.hInstance = GetModuleHandleW( NULL );
  1705. wndclass.hIcon = 0;
  1706. wndclass.hCursor = LoadCursorW( 0, (const WCHAR *)IDC_ARROW );
  1707. wndclass.hbrBackground = GetStockObject( BLACK_BRUSH );
  1708. wndclass.lpszMenuName = NULL;
  1709. wndclass.lpszClassName = L"WineConFontPreview";
  1710. RegisterClassW( &wndclass );
  1711. wndclass.style = 0;
  1712. wndclass.lpfnWndProc = color_preview_proc;
  1713. wndclass.cbClsExtra = 0;
  1714. wndclass.cbWndExtra = sizeof(DWORD);
  1715. wndclass.hInstance = GetModuleHandleW( NULL );
  1716. wndclass.hIcon = 0;
  1717. wndclass.hCursor = LoadCursorW( 0, (const WCHAR *)IDC_ARROW );
  1718. wndclass.hbrBackground = GetStockObject( BLACK_BRUSH );
  1719. wndclass.lpszMenuName = NULL;
  1720. wndclass.lpszClassName = L"WineConColorPreview";
  1721. RegisterClassW( &wndclass );
  1722. memset( &psp, 0, sizeof(psp) );
  1723. psp.dwSize = sizeof(psp);
  1724. psp.dwFlags = 0;
  1725. psp.hInstance = wndclass.hInstance;
  1726. psp.lParam = (LPARAM)&di;
  1727. psp.u.pszTemplate = MAKEINTRESOURCEW(IDD_OPTION);
  1728. psp.pfnDlgProc = option_dialog_proc;
  1729. pages[0] = CreatePropertySheetPageW( &psp );
  1730. psp.u.pszTemplate = MAKEINTRESOURCEW(IDD_FONT);
  1731. psp.pfnDlgProc = font_dialog_proc;
  1732. pages[1] = CreatePropertySheetPageW( &psp );
  1733. psp.u.pszTemplate = MAKEINTRESOURCEW(IDD_CONFIG);
  1734. psp.pfnDlgProc = config_dialog_proc;
  1735. pages[2] = CreatePropertySheetPageW( &psp );
  1736. memset( &header, 0, sizeof(header) );
  1737. header.dwSize = sizeof(header);
  1738. if (!LoadStringW( GetModuleHandleW( NULL ),
  1739. current ? IDS_DLG_TIT_CURRENT : IDS_DLG_TIT_DEFAULT,
  1740. buff, ARRAY_SIZE(buff) ))
  1741. wcscpy( buff, L"Setup" );
  1742. header.pszCaption = buff;
  1743. header.nPages = 3;
  1744. header.hwndParent = console->win;
  1745. header.u3.phpage = pages;
  1746. header.dwFlags = PSH_NOAPPLYNOW;
  1747. PropertySheetW( &header );
  1748. if (!memcmp( &prev_config, &di.config, sizeof(prev_config) ))
  1749. return TRUE;
  1750. TRACE( "%s\n", debugstr_config(&di.config) );
  1751. if (!save)
  1752. {
  1753. switch (DialogBoxW( GetModuleHandleW( NULL ), MAKEINTRESOURCEW(IDD_SAVE_SETTINGS),
  1754. console->win, save_dialog_proc ))
  1755. {
  1756. case IDC_SAV_SAVE:
  1757. save = TRUE;
  1758. modify_session = TRUE;
  1759. break;
  1760. case IDC_SAV_SESSION:
  1761. modify_session = TRUE;
  1762. break;
  1763. default:
  1764. ERR( "dialog failed\n" );
  1765. /* fall through */
  1766. case IDCANCEL:
  1767. modify_session = FALSE;
  1768. save = FALSE;
  1769. break;
  1770. }
  1771. }
  1772. if (modify_session)
  1773. {
  1774. apply_config( console, &di.config );
  1775. update_window( di.console );
  1776. }
  1777. if (save)
  1778. save_config( current ? console->window->config_key : NULL, &di.config );
  1779. return TRUE;
  1780. }
  1781. static void resize_window( struct console *console, int width, int height )
  1782. {
  1783. struct console_config config;
  1784. current_config( console, &config );
  1785. config.win_width = width;
  1786. config.win_height = height;
  1787. /* auto size screen-buffer if it's now smaller than window */
  1788. if (config.sb_width < config.win_width)
  1789. config.sb_width = config.win_width;
  1790. if (config.sb_height < config.win_height)
  1791. config.sb_height = config.win_height;
  1792. /* and reset window pos so that we don't display outside of the screen-buffer */
  1793. if (config.win_pos.X + config.win_width > config.sb_width)
  1794. config.win_pos.X = config.sb_width - config.win_width;
  1795. if (config.win_pos.Y + config.win_height > config.sb_height)
  1796. config.win_pos.Y = config.sb_height - config.win_height;
  1797. apply_config( console, &config );
  1798. }
  1799. /* grays / ungrays the menu items according to their state */
  1800. static void set_menu_details( struct console *console, HMENU menu )
  1801. {
  1802. EnableMenuItem( menu, IDS_COPY, MF_BYCOMMAND |
  1803. (console->window->in_selection ? MF_ENABLED : MF_GRAYED) );
  1804. EnableMenuItem( menu, IDS_PASTE, MF_BYCOMMAND |
  1805. (IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED) );
  1806. EnableMenuItem( menu, IDS_SCROLL, MF_BYCOMMAND | MF_GRAYED );
  1807. EnableMenuItem( menu, IDS_SEARCH, MF_BYCOMMAND | MF_GRAYED );
  1808. }
  1809. static BOOL fill_menu( HMENU menu, BOOL sep )
  1810. {
  1811. HINSTANCE module = GetModuleHandleW( NULL );
  1812. HMENU sub_menu;
  1813. WCHAR buff[256];
  1814. if (!menu) return FALSE;
  1815. sub_menu = CreateMenu();
  1816. if (!sub_menu) return FALSE;
  1817. LoadStringW( module, IDS_MARK, buff, ARRAY_SIZE(buff) );
  1818. InsertMenuW( sub_menu, -1, MF_BYPOSITION|MF_STRING, IDS_MARK, buff );
  1819. LoadStringW( module, IDS_COPY, buff, ARRAY_SIZE(buff) );
  1820. InsertMenuW( sub_menu, -1, MF_BYPOSITION|MF_STRING, IDS_COPY, buff );
  1821. LoadStringW( module, IDS_PASTE, buff, ARRAY_SIZE(buff) );
  1822. InsertMenuW( sub_menu, -1, MF_BYPOSITION|MF_STRING, IDS_PASTE, buff );
  1823. LoadStringW( module, IDS_SELECTALL, buff, ARRAY_SIZE(buff) );
  1824. InsertMenuW( sub_menu, -1, MF_BYPOSITION|MF_STRING, IDS_SELECTALL, buff );
  1825. LoadStringW( module, IDS_SCROLL, buff, ARRAY_SIZE(buff) );
  1826. InsertMenuW( sub_menu, -1, MF_BYPOSITION|MF_STRING, IDS_SCROLL, buff );
  1827. LoadStringW( module, IDS_SEARCH, buff, ARRAY_SIZE(buff) );
  1828. InsertMenuW( sub_menu, -1, MF_BYPOSITION|MF_STRING, IDS_SEARCH, buff );
  1829. if (sep) InsertMenuW( menu, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL );
  1830. LoadStringW( module, IDS_EDIT, buff, ARRAY_SIZE(buff) );
  1831. InsertMenuW( menu, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR)sub_menu, buff );
  1832. LoadStringW( module, IDS_DEFAULT, buff, ARRAY_SIZE(buff) );
  1833. InsertMenuW( menu, -1, MF_BYPOSITION|MF_STRING, IDS_DEFAULT, buff );
  1834. LoadStringW( module, IDS_PROPERTIES, buff, ARRAY_SIZE(buff) );
  1835. InsertMenuW( menu, -1, MF_BYPOSITION|MF_STRING, IDS_PROPERTIES, buff );
  1836. return TRUE;
  1837. }
  1838. static LRESULT window_create( HWND hwnd, const CREATESTRUCTW *create )
  1839. {
  1840. struct console *console = create->lpCreateParams;
  1841. HMENU sys_menu;
  1842. TRACE( "%p\n", hwnd );
  1843. SetWindowLongPtrW( hwnd, 0, (DWORD_PTR)console );
  1844. console->win = hwnd;
  1845. sys_menu = GetSystemMenu( hwnd, FALSE );
  1846. if (!sys_menu) return 0;
  1847. console->window->popup_menu = CreatePopupMenu();
  1848. if (!console->window->popup_menu) return 0;
  1849. fill_menu( sys_menu, TRUE );
  1850. fill_menu( console->window->popup_menu, FALSE );
  1851. console->window->mem_dc = CreateCompatibleDC( 0 );
  1852. return 0;
  1853. }
  1854. static LRESULT WINAPI window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
  1855. {
  1856. struct console *console = (struct console *)GetWindowLongPtrW( hwnd, 0 );
  1857. switch (msg)
  1858. {
  1859. case WM_CREATE:
  1860. return window_create( hwnd, (const CREATESTRUCTW *)lparam );
  1861. case WM_DESTROY:
  1862. console->win = NULL;
  1863. PostQuitMessage( 0 );
  1864. break;
  1865. case WM_TIMER:
  1866. case WM_UPDATE_CONFIG:
  1867. if (console->window->update_state == UPDATE_PENDING)
  1868. update_window( console );
  1869. break;
  1870. case WM_PAINT:
  1871. {
  1872. PAINTSTRUCT ps;
  1873. BeginPaint( console->win, &ps );
  1874. BitBlt( ps.hdc, 0, 0,
  1875. (console->active->win.right - console->active->win.left + 1) * console->active->font.width,
  1876. (console->active->win.bottom - console->active->win.top + 1) * console->active->font.height,
  1877. console->window->mem_dc,
  1878. console->active->win.left * console->active->font.width,
  1879. console->active->win.top * console->active->font.height,
  1880. SRCCOPY );
  1881. if (console->window->in_selection) update_selection( console, ps.hdc );
  1882. EndPaint( console->win, &ps );
  1883. break;
  1884. }
  1885. case WM_SHOWWINDOW:
  1886. if (wparam)
  1887. update_window( console );
  1888. else
  1889. {
  1890. if (console->window->bitmap) DeleteObject( console->window->bitmap );
  1891. console->window->bitmap = NULL;
  1892. }
  1893. break;
  1894. case WM_KEYDOWN:
  1895. case WM_KEYUP:
  1896. if (console->window->in_selection)
  1897. handle_selection_key( console, msg == WM_KEYDOWN, wparam, lparam );
  1898. else
  1899. record_key_input( console, msg == WM_KEYDOWN, wparam, lparam );
  1900. break;
  1901. case WM_SYSKEYDOWN:
  1902. case WM_SYSKEYUP:
  1903. record_key_input( console, msg == WM_SYSKEYDOWN, wparam, lparam );
  1904. break;
  1905. case WM_LBUTTONDOWN:
  1906. if (console->window->quick_edit || console->window->in_selection)
  1907. {
  1908. if (console->window->in_selection)
  1909. update_selection( console, 0 );
  1910. if (console->window->quick_edit && console->window->in_selection)
  1911. {
  1912. console->window->in_selection = FALSE;
  1913. }
  1914. else
  1915. {
  1916. console->window->selection_end = get_cell( console, lparam );
  1917. console->window->selection_start = console->window->selection_end;
  1918. SetCapture( console->win );
  1919. update_selection( console, 0 );
  1920. console->window->in_selection = TRUE;
  1921. }
  1922. }
  1923. else
  1924. {
  1925. record_mouse_input( console, get_cell(console, lparam), wparam, 0 );
  1926. }
  1927. break;
  1928. case WM_MOUSEMOVE:
  1929. if (console->window->quick_edit || console->window->in_selection)
  1930. {
  1931. if (GetCapture() == console->win && console->window->in_selection &&
  1932. (wparam & MK_LBUTTON))
  1933. {
  1934. move_selection( console, console->window->selection_start,
  1935. get_cell(console, lparam) );
  1936. }
  1937. }
  1938. else
  1939. {
  1940. record_mouse_input( console, get_cell(console, lparam), wparam, MOUSE_MOVED );
  1941. }
  1942. break;
  1943. case WM_LBUTTONUP:
  1944. if (console->window->quick_edit || console->window->in_selection)
  1945. {
  1946. if (GetCapture() == console->win && console->window->in_selection)
  1947. {
  1948. move_selection( console, console->window->selection_start,
  1949. get_cell(console, lparam) );
  1950. ReleaseCapture();
  1951. }
  1952. }
  1953. else
  1954. {
  1955. record_mouse_input( console, get_cell(console, lparam), wparam, 0 );
  1956. }
  1957. break;
  1958. case WM_RBUTTONDOWN:
  1959. if ((wparam & (MK_CONTROL|MK_SHIFT)) == console->window->menu_mask)
  1960. {
  1961. POINT pt;
  1962. pt.x = (short)LOWORD(lparam);
  1963. pt.y = (short)HIWORD(lparam);
  1964. ClientToScreen( hwnd, &pt );
  1965. set_menu_details( console, console->window->popup_menu );
  1966. TrackPopupMenu( console->window->popup_menu, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RIGHTBUTTON,
  1967. pt.x, pt.y, 0, hwnd, NULL );
  1968. }
  1969. else
  1970. {
  1971. record_mouse_input( console, get_cell(console, lparam), wparam, 0 );
  1972. }
  1973. break;
  1974. case WM_RBUTTONUP:
  1975. /* no need to track for rbutton up when opening the popup... the event will be
  1976. * swallowed by TrackPopupMenu */
  1977. case WM_MBUTTONDOWN:
  1978. case WM_MBUTTONUP:
  1979. record_mouse_input( console, get_cell(console, lparam), wparam, 0 );
  1980. break;
  1981. case WM_LBUTTONDBLCLK:
  1982. case WM_MBUTTONDBLCLK:
  1983. case WM_RBUTTONDBLCLK:
  1984. record_mouse_input( console, get_cell(console, lparam), wparam, DOUBLE_CLICK );
  1985. break;
  1986. case WM_SETFOCUS:
  1987. if (console->active->cursor_visible)
  1988. {
  1989. CreateCaret( console->win, console->window->cursor_bitmap,
  1990. console->active->font.width, console->active->font.height );
  1991. update_window_cursor( console );
  1992. }
  1993. break;
  1994. case WM_KILLFOCUS:
  1995. if (console->active->cursor_visible)
  1996. DestroyCaret();
  1997. break;
  1998. case WM_SIZE:
  1999. if (console->window->update_state != UPDATE_BUSY)
  2000. resize_window( console,
  2001. max( LOWORD(lparam) / console->active->font.width, 20 ),
  2002. max( HIWORD(lparam) / console->active->font.height, 20 ));
  2003. break;
  2004. case WM_HSCROLL:
  2005. {
  2006. int win_width = console->active->win.right - console->active->win.left + 1;
  2007. int x = console->active->win.left;
  2008. switch (LOWORD(wparam))
  2009. {
  2010. case SB_PAGEUP: x -= 8; break;
  2011. case SB_PAGEDOWN: x += 8; break;
  2012. case SB_LINEUP: x--; break;
  2013. case SB_LINEDOWN: x++; break;
  2014. case SB_THUMBTRACK: x = HIWORD(wparam); break;
  2015. default: break;
  2016. }
  2017. x = min( max( x, 0 ), console->active->width - win_width );
  2018. if (x != console->active->win.left)
  2019. {
  2020. console->active->win.left = x;
  2021. console->active->win.right = x + win_width - 1;
  2022. update_window( console );
  2023. }
  2024. break;
  2025. }
  2026. case WM_MOUSEWHEEL:
  2027. if (console->active->height <= console->active->win.bottom - console->active->win.top + 1)
  2028. {
  2029. record_mouse_input(console, get_cell(console, lparam), wparam, MOUSE_WHEELED);
  2030. break;
  2031. }
  2032. /* else fallthrough */
  2033. case WM_VSCROLL:
  2034. {
  2035. int win_height = console->active->win.bottom - console->active->win.top + 1;
  2036. int y = console->active->win.top;
  2037. if (msg == WM_MOUSEWHEEL)
  2038. {
  2039. UINT scroll_lines = 3;
  2040. SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &scroll_lines, 0 );
  2041. scroll_lines *= -GET_WHEEL_DELTA_WPARAM(wparam) / WHEEL_DELTA;
  2042. y += scroll_lines;
  2043. }
  2044. else
  2045. {
  2046. switch (LOWORD(wparam))
  2047. {
  2048. case SB_PAGEUP: y -= 8; break;
  2049. case SB_PAGEDOWN: y += 8; break;
  2050. case SB_LINEUP: y--; break;
  2051. case SB_LINEDOWN: y++; break;
  2052. case SB_THUMBTRACK: y = HIWORD(wparam); break;
  2053. default: break;
  2054. }
  2055. }
  2056. y = min( max( y, 0 ), console->active->height - win_height );
  2057. if (y != console->active->win.top)
  2058. {
  2059. console->active->win.top = y;
  2060. console->active->win.bottom = y + win_height - 1;
  2061. update_window( console );
  2062. }
  2063. break;
  2064. }
  2065. case WM_SYSCOMMAND:
  2066. switch (wparam)
  2067. {
  2068. case IDS_DEFAULT:
  2069. config_dialog( console, FALSE );
  2070. break;
  2071. case IDS_PROPERTIES:
  2072. config_dialog( console, TRUE );
  2073. break;
  2074. default:
  2075. return DefWindowProcW( hwnd, msg, wparam, lparam );
  2076. }
  2077. break;
  2078. case WM_COMMAND:
  2079. switch (wparam)
  2080. {
  2081. case IDS_DEFAULT:
  2082. config_dialog( console, FALSE );
  2083. break;
  2084. case IDS_PROPERTIES:
  2085. config_dialog( console, TRUE );
  2086. break;
  2087. case IDS_MARK:
  2088. console->window->selection_start.X = console->window->selection_start.Y = 0;
  2089. console->window->selection_end.X = console->window->selection_end.Y = 0;
  2090. update_selection( console, 0 );
  2091. console->window->in_selection = TRUE;
  2092. break;
  2093. case IDS_COPY:
  2094. if (console->window->in_selection)
  2095. {
  2096. console->window->in_selection = FALSE;
  2097. update_selection( console, 0 );
  2098. copy_selection( console );
  2099. }
  2100. break;
  2101. case IDS_PASTE:
  2102. paste_clipboard( console );
  2103. break;
  2104. case IDS_SELECTALL:
  2105. console->window->selection_start.X = console->window->selection_start.Y = 0;
  2106. console->window->selection_end.X = console->active->width - 1;
  2107. console->window->selection_end.Y = console->active->height - 1;
  2108. update_selection( console, 0 );
  2109. console->window->in_selection = TRUE;
  2110. break;
  2111. case IDS_SCROLL:
  2112. case IDS_SEARCH:
  2113. FIXME( "Unhandled yet command: %lx\n", wparam );
  2114. break;
  2115. default:
  2116. return DefWindowProcW( hwnd, msg, wparam, lparam );
  2117. }
  2118. break;
  2119. case WM_INITMENUPOPUP:
  2120. if (!HIWORD(lparam)) return DefWindowProcW( hwnd, msg, wparam, lparam );
  2121. set_menu_details( console, GetSystemMenu(console->win, FALSE) );
  2122. break;
  2123. default:
  2124. return DefWindowProcW( hwnd, msg, wparam, lparam );
  2125. }
  2126. return 0;
  2127. }
  2128. void update_window_config( struct console *console, BOOL delay )
  2129. {
  2130. const int delay_timeout = 50;
  2131. if (!console->win || console->window->update_state != UPDATE_NONE) return;
  2132. console->window->update_state = UPDATE_PENDING;
  2133. if (delay)
  2134. SetTimer( console->win, 1, delay_timeout, NULL );
  2135. else
  2136. PostMessageW( console->win, WM_UPDATE_CONFIG, 0, 0 );
  2137. }
  2138. void update_window_region( struct console *console, const RECT *update )
  2139. {
  2140. RECT *window_rect = &console->window->update;
  2141. window_rect->left = min( window_rect->left, update->left );
  2142. window_rect->top = min( window_rect->top, update->top );
  2143. window_rect->right = max( window_rect->right, update->right );
  2144. window_rect->bottom = max( window_rect->bottom, update->bottom );
  2145. update_window_config( console, TRUE );
  2146. }
  2147. BOOL init_window( struct console *console )
  2148. {
  2149. struct console_config config;
  2150. WNDCLASSW wndclass;
  2151. STARTUPINFOW si;
  2152. CHARSETINFO ci;
  2153. static struct console_window console_window;
  2154. console->window = &console_window;
  2155. if (!TranslateCharsetInfo( (DWORD *)(INT_PTR)GetACP(), &ci, TCI_SRCCODEPAGE ))
  2156. return FALSE;
  2157. console->window->ui_charset = ci.ciCharset;
  2158. GetStartupInfoW(&si);
  2159. if (si.lpTitle)
  2160. {
  2161. size_t i, title_len = wcslen( si.lpTitle );
  2162. if (!(console->window->config_key = malloc( (title_len + 1) * sizeof(WCHAR) )))
  2163. return FALSE;
  2164. for (i = 0; i < title_len; i++)
  2165. console->window->config_key[i] = si.lpTitle[i] == '\\' ? '_' : si.lpTitle[i];
  2166. console->window->config_key[title_len] = 0;
  2167. }
  2168. load_config( console->window->config_key, &config );
  2169. if (si.dwFlags & STARTF_USECOUNTCHARS)
  2170. {
  2171. config.sb_width = si.dwXCountChars;
  2172. config.sb_height = si.dwYCountChars;
  2173. }
  2174. if (si.dwFlags & STARTF_USEFILLATTRIBUTE)
  2175. config.attr = si.dwFillAttribute;
  2176. wndclass.style = CS_DBLCLKS;
  2177. wndclass.lpfnWndProc = window_proc;
  2178. wndclass.cbClsExtra = 0;
  2179. wndclass.cbWndExtra = sizeof(DWORD_PTR);
  2180. wndclass.hInstance = GetModuleHandleW(NULL);
  2181. wndclass.hIcon = LoadIconW( 0, (const WCHAR *)IDI_WINLOGO );
  2182. wndclass.hCursor = LoadCursorW( 0, (const WCHAR *)IDC_ARROW );
  2183. wndclass.hbrBackground = GetStockObject( BLACK_BRUSH );
  2184. wndclass.lpszMenuName = NULL;
  2185. wndclass.lpszClassName = L"WineConsoleClass";
  2186. RegisterClassW(&wndclass);
  2187. if (!CreateWindowW( wndclass.lpszClassName, NULL,
  2188. WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|
  2189. WS_MAXIMIZEBOX|WS_HSCROLL|WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT,
  2190. 0, 0, 0, 0, wndclass.hInstance, console ))
  2191. return FALSE;
  2192. apply_config( console, &config );
  2193. return TRUE;
  2194. }