Skip to content

Commit eabf578

Browse files
elwood@elwood-PCelwood@elwood-PC
elwood@elwood-PC
authored and
elwood@elwood-PC
committed
Initial commit.
0 parents  commit eabf578

21 files changed

+1267
-0
lines changed

.hgignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
syntax: glob
2+
_ReSharper.ConsoleFramework
3+
TestProject1/bin/Debug/
4+
TestProject1/obj/Debug/
5+
ConsoleFramework/bin/
6+
ConsoleFramework/obj/
7+
ConsoleFramework.6.0.ReSharper.user

ConsoleFramework.sln

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 11.00
3+
# Visual Studio 2010
4+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleFramework", "ConsoleFramework\ConsoleFramework.csproj", "{2A59C284-2995-4F37-8D65-411C25C85493}"
5+
EndProject
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject1", "TestProject1\TestProject1.csproj", "{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F4C77240-72FA-43DD-9AF6-8B7B8B10E9ED}"
9+
ProjectSection(SolutionItems) = preProject
10+
ConsoleFramework.vsmdi = ConsoleFramework.vsmdi
11+
Local.testsettings = Local.testsettings
12+
TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
13+
EndProjectSection
14+
EndProject
15+
Global
16+
GlobalSection(TestCaseManagementSettings) = postSolution
17+
CategoryFile = ConsoleFramework.vsmdi
18+
EndGlobalSection
19+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
20+
Debug|Any CPU = Debug|Any CPU
21+
Debug|Mixed Platforms = Debug|Mixed Platforms
22+
Debug|x86 = Debug|x86
23+
Release|Any CPU = Release|Any CPU
24+
Release|Mixed Platforms = Release|Mixed Platforms
25+
Release|x86 = Release|x86
26+
EndGlobalSection
27+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
28+
{2A59C284-2995-4F37-8D65-411C25C85493}.Debug|Any CPU.ActiveCfg = Debug|x86
29+
{2A59C284-2995-4F37-8D65-411C25C85493}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
30+
{2A59C284-2995-4F37-8D65-411C25C85493}.Debug|Mixed Platforms.Build.0 = Debug|x86
31+
{2A59C284-2995-4F37-8D65-411C25C85493}.Debug|x86.ActiveCfg = Debug|x86
32+
{2A59C284-2995-4F37-8D65-411C25C85493}.Debug|x86.Build.0 = Debug|x86
33+
{2A59C284-2995-4F37-8D65-411C25C85493}.Release|Any CPU.ActiveCfg = Release|x86
34+
{2A59C284-2995-4F37-8D65-411C25C85493}.Release|Mixed Platforms.ActiveCfg = Release|x86
35+
{2A59C284-2995-4F37-8D65-411C25C85493}.Release|Mixed Platforms.Build.0 = Release|x86
36+
{2A59C284-2995-4F37-8D65-411C25C85493}.Release|x86.ActiveCfg = Release|x86
37+
{2A59C284-2995-4F37-8D65-411C25C85493}.Release|x86.Build.0 = Release|x86
38+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
41+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
42+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Debug|x86.ActiveCfg = Debug|Any CPU
43+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Release|Any CPU.ActiveCfg = Release|Any CPU
44+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Release|Any CPU.Build.0 = Release|Any CPU
45+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
46+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Release|Mixed Platforms.Build.0 = Release|Any CPU
47+
{1579CF00-4DFB-48E0-B6C0-098DA0FC9F41}.Release|x86.ActiveCfg = Release|Any CPU
48+
EndGlobalSection
49+
GlobalSection(SolutionProperties) = preSolution
50+
HideSolutionNode = FALSE
51+
EndGlobalSection
52+
EndGlobal

ConsoleFramework.suo

36.5 KB
Binary file not shown.

ConsoleFramework.vsmdi

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
3+
<TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
4+
<RunConfiguration id="7ea4b1b8-8dfb-4cee-ab38-ea7ccb26cf60" name="Local" storage="local.testsettings" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
5+
</TestList>
6+
</TestLists>
+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System;
2+
using System.Threading;
3+
using ConsoleFramework.Native;
4+
5+
namespace ConsoleFramework
6+
{
7+
public sealed class ConsoleApplication : IDisposable {
8+
9+
private ConsoleApplication() {
10+
}
11+
12+
private static volatile ConsoleApplication instance;
13+
private static readonly object syncRoot = new object();
14+
public static ConsoleApplication Instance {
15+
get {
16+
if (instance == null) {
17+
lock (syncRoot) {
18+
if (instance == null) {
19+
instance = new ConsoleApplication();
20+
}
21+
}
22+
}
23+
return instance;
24+
}
25+
}
26+
27+
private IntPtr stdInputHandle;
28+
private IntPtr stdOutputHandle;
29+
private readonly EventWaitHandle exitWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
30+
private readonly ConsoleDispatcher dispatcher = new ConsoleDispatcher();
31+
public ConsoleDispatcher Dispatcher {
32+
get {
33+
return dispatcher;
34+
}
35+
}
36+
37+
public void Exit() {
38+
exitWaitHandle.Set();
39+
}
40+
41+
public void Run() {
42+
//
43+
stdInputHandle = NativeMethods.GetStdHandle(StdHandleType.STD_INPUT_HANDLE);
44+
stdOutputHandle = NativeMethods.GetStdHandle(StdHandleType.STD_OUTPUT_HANDLE);
45+
IntPtr[] handles = new[] {
46+
exitWaitHandle.SafeWaitHandle.DangerousGetHandle(),
47+
stdInputHandle
48+
};
49+
// small test of physical canvas, todo : remove after tests
50+
PhysicalCanvas canvas = new PhysicalCanvas(80, 25, stdOutputHandle);
51+
for (int x = 0; x < 80; x++ ) {
52+
for (int y = 0; y < 25; y++) {
53+
if (x == 40) {
54+
canvas[x][y].UnicodeChar = '8';
55+
canvas[x][y].Attributes = CHAR_ATTRIBUTES.BACKGROUND_RED;
56+
}
57+
else {
58+
canvas[x][y].AsciiChar = '0';
59+
canvas[x][y].Attributes = CHAR_ATTRIBUTES.FOREGROUND_BLUE | CHAR_ATTRIBUTES.FOREGROUND_GREEN;
60+
}
61+
62+
}
63+
}
64+
canvas.Flush();
65+
66+
while (true) {
67+
uint waitResult = NativeMethods.WaitForMultipleObjects(2, handles, false, NativeMethods.INFINITE);
68+
if (waitResult == 0) {
69+
break;
70+
}
71+
if (waitResult == 1) {
72+
processInput();
73+
continue;
74+
}
75+
// if we received WAIT_TIMEOUT or WAIT_FAILED
76+
if (waitResult == 0x00000102 || waitResult == 0xFFFFFFFF) {
77+
throw new InvalidOperationException("Invalid wait result of WaitForMultipleObjects.");
78+
}
79+
}
80+
}
81+
82+
private void processInput() {
83+
INPUT_RECORD[] buffer = new INPUT_RECORD[10];
84+
uint read;
85+
bool bReaded = NativeMethods.ReadConsoleInput(stdInputHandle, buffer, (uint) buffer.Length, out read);
86+
if (!bReaded) {
87+
throw new InvalidOperationException("ReadConsoleInput method failed.");
88+
}
89+
for (int i = 0; i < read; ++i) {
90+
processInputEvent(buffer[i]);
91+
}
92+
}
93+
94+
private void processInputEvent(INPUT_RECORD inputRecord) {
95+
// todo : remove after tests
96+
if (inputRecord.EventType == EventType.MOUSE_EVENT) {
97+
if (inputRecord.MouseEvent.dwButtonState == MouseButtonState.RIGHTMOST_BUTTON_PRESSED && inputRecord.MouseEvent.dwEventFlags == MouseEventFlags.DOUBLE_CLICK) {
98+
this.Exit();
99+
}
100+
}
101+
//
102+
dispatcher.DispatchInputEvent(inputRecord);
103+
}
104+
105+
private void dispose(bool isDisposing) {
106+
if (isDisposing) {
107+
if (exitWaitHandle != null) {
108+
exitWaitHandle.Dispose();
109+
}
110+
}
111+
}
112+
113+
public void Dispose() {
114+
dispose(true);
115+
GC.SuppressFinalize(this);
116+
}
117+
118+
~ConsoleApplication() {
119+
dispose(false);
120+
}
121+
}
122+
}

ConsoleFramework/ConsoleDispatcher.cs

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using ConsoleFramework.Native;
4+
5+
namespace ConsoleFramework
6+
{
7+
/// <summary>
8+
/// Хранит в себе стек окон, по запросам, приходящим из ConsoleApplication, обрабатывает ввод пользователя,
9+
/// вычисляя, какому окно был предназначен этот ввод и перенаправляя событие ввода этому окну.
10+
/// </summary>
11+
public sealed class ConsoleDispatcher : IElementsVisibilityAware {
12+
public List<FrameworkElement> WindowsStack = new List<FrameworkElement>();
13+
private readonly Dictionary<FrameworkElement, Visibility> visibilityCache = new Dictionary<FrameworkElement, Visibility>();
14+
public bool visibilityAnalyzeCompleted = false;
15+
16+
public FrameworkElementVisibility GetElementVisibility(FrameworkElement element) {
17+
return getElementVisibilityCore(element).Type;
18+
}
19+
20+
private Visibility getElementVisibilityCore(FrameworkElement element) {
21+
if (!visibilityAnalyzeCompleted) {
22+
AnalyzeVisibility();
23+
}
24+
Visibility res;
25+
if (!visibilityCache.TryGetValue(element, out res)) {
26+
throw new InvalidOperationException("Element was not found at visibility cache.");
27+
}
28+
return res;
29+
}
30+
31+
public bool IsPointOfElementVisible(int x, int y, FrameworkElement element) {
32+
if (x < 0 || y < 0 || x > element.Width || y > element.Height) {
33+
throw new ArgumentException("Coords to point are invalid.");
34+
}
35+
//
36+
Visibility visibility = getElementVisibilityCore(element);
37+
if (visibility.Type == FrameworkElementVisibility.FullVisible) {
38+
return true;
39+
}
40+
if (visibility.Type == FrameworkElementVisibility.Hidden) {
41+
return false;
42+
}
43+
//
44+
List<SMALL_RECT> overlappedRegions = visibility.OverlappedRegions;
45+
foreach (SMALL_RECT rect in overlappedRegions) {
46+
// если точка лежит внутри одной из областей, перекрытых верхними окнами - false
47+
if (x >= rect.Left && x <= rect.Right && y >= rect.Top && y <= rect.Bottom) {
48+
return false;
49+
}
50+
}
51+
return true;
52+
}
53+
54+
public void DispatchInputEvent(INPUT_RECORD inputRecord) {
55+
//
56+
}
57+
58+
public void RegisterWindow(FrameworkElement element) {
59+
WindowsStack.Add(element);
60+
if (visibilityAnalyzeCompleted) {
61+
visibilityAnalyzeCompleted = false;
62+
}
63+
}
64+
65+
public void AnalyzeVisibility() {
66+
if (visibilityAnalyzeCompleted) {
67+
return;
68+
}
69+
//
70+
if (WindowsStack.Count > 0) {
71+
for (int i = WindowsStack.Count - 1; i >= 0; --i) {
72+
FrameworkElement element = WindowsStack[i];
73+
Visibility visibility;
74+
if (!visibilityCache.TryGetValue(element, out visibility)) {
75+
visibility = new Visibility(FrameworkElementVisibility.FullVisible);
76+
visibilityCache.Add(element, visibility);
77+
} else {
78+
visibility.Type = FrameworkElementVisibility.FullVisible;
79+
visibility.OverlappedRegions.Clear();
80+
}
81+
//
82+
for (int j = WindowsStack.Count - 1; j > i; --j) {
83+
FrameworkElement element2 = WindowsStack[j];
84+
if (visibilityCache[element2].Type == FrameworkElementVisibility.Hidden) {
85+
continue;
86+
}
87+
SMALL_RECT? overlappingRegion = getOverlappingRegion(element, element2);
88+
if (overlappingRegion != null) {
89+
SMALL_RECT rect = overlappingRegion.Value;
90+
if (rect.Left == 0 && rect.Top == 0 && rect.Right == element.Width && rect.Bottom == element.Height) {
91+
visibility.Type = FrameworkElementVisibility.Hidden;
92+
break;
93+
}
94+
visibility.OverlappedRegions.Add(rect);
95+
visibility.Type = FrameworkElementVisibility.PartiallyVisible;
96+
}
97+
}
98+
}
99+
}
100+
visibilityAnalyzeCompleted = true;
101+
}
102+
103+
/// <summary>
104+
/// Проверяет, перекрывает ли b, находящийся сверху, элемент a, находящийся под ним.
105+
/// Возвращает координаты прямоугольной области относительно элемента a, если b его перекрывает.
106+
/// Или null в случае, если b не перекрывает a вообще.
107+
/// </summary>
108+
public static SMALL_RECT? getOverlappingRegion(FrameworkElement a, FrameworkElement b) {
109+
SMALL_RECT? res = null;
110+
//
111+
bool firstIsLeft = a.X <= b.X;
112+
FrameworkElement leftElement = firstIsLeft ? a : b;
113+
FrameworkElement rightElement = firstIsLeft ? b : a;
114+
// вертикальные составляющие прямоугольников - это области между двумя параллельными прямыми,
115+
// одна из которых образована левой границей фигуры, а другая - правой
116+
bool verticalIntersects = leftElement.X <= rightElement.X &&
117+
rightElement.X <= leftElement.X + leftElement.Width;
118+
bool firstIsTop = a.Y <= b.Y;
119+
FrameworkElement topElement = firstIsTop ? a : b;
120+
FrameworkElement bottomElement = firstIsTop ? b : a;
121+
// аналогично определяются горизонтальные составляющие прямоугольников
122+
bool horizontalIntersects = topElement.Y <= bottomElement.Y &&
123+
bottomElement.Y <= topElement.Y + topElement.Height;
124+
// если пересекаются и горизонтальные, и вертикальные составляющие - мы имеем
125+
// пересечение прямоугольников, и можем получить координаты области пересечения
126+
if (verticalIntersects && horizontalIntersects) {
127+
// получаем глобальные координаты области пересечения
128+
int intersectionLeft = rightElement.X;
129+
int intersectionRight = Math.Min(leftElement.X + leftElement.Width, rightElement.X + rightElement.Width);
130+
int intersectionTop = bottomElement.Y;
131+
int intersectionBottom = Math.Min(topElement.Y + topElement.Height,
132+
bottomElement.Y + bottomElement.Height);
133+
// возвращаем координаты области пересечения относительно a
134+
res = new SMALL_RECT((short) (intersectionLeft - a.X), (short) (intersectionTop - a.Y),
135+
(short) (intersectionRight - a.X), (short) (intersectionBottom - a.Y));
136+
}
137+
return res;
138+
}
139+
}
140+
141+
internal class Visibility {
142+
private List<SMALL_RECT> overlappedRegions;
143+
public List<SMALL_RECT> OverlappedRegions {
144+
get {
145+
return overlappedRegions ?? (overlappedRegions = new List<SMALL_RECT>());
146+
}
147+
}
148+
149+
public FrameworkElementVisibility Type {
150+
get;
151+
set;
152+
}
153+
154+
public Visibility() {
155+
}
156+
157+
public Visibility(FrameworkElementVisibility type) {
158+
this.Type = type;
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)