https://wiki.qt.io/Qt_for_WebAssembly

安装 Emscripten SDK

从GitHub下载emsdk

https://github.com/emscripten-core/emsdk

Qt版本和Emscripten版本的对应关系参考文首的链接。

安装1.38.27版本的Emscripten

1
./emsdk install sdk-fastcomp-1.38.27-64bit

编译Qt源代码

下载Qt5.14源代码http://download.qt.io/archive/qt/

配置源代码

1
./configure -xplatform wasm-emscripten -nomake examples -prefix $PWD/qtbase
阅读全文 »

《Google软件测试之道》笔记

在Google,软件测试团队归属于一个被称为“工程生产力”的中心组织部门,这个部门的职责横跨开发测试人员使用工具的研发、产品发布和各种级别的测试,从单元级别的测试到探索性级别的测试。

Google是一家以创新和速度为基础的公司,快速地发布有用的代码(如果失败,也只有少数早期用户会失望)、迭代地增加早期用户希望使用的功能(最大化用户反馈)。

在整个公司,我们只有非常少的专职测试人员。

Google的测试团队并非雄兵百万,我们更像是小而精的特种部队。

测试人员的稀缺会导致测试资源变得非常昂贵,因此,我们的原则就是让这些稀缺且聪明的测试员工保持昂扬的斗志和充沛的精力。

阅读全文 »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/usr/bin/env perl
#############################################################################
##
## Copyright (C) 2016 The Qt Company Ltd.
## Contact: https://www.qt.io/licensing/
##
## This file is part of the test suite of the Qt Toolkit.
##
## $QT_BEGIN_LICENSE:GPL-EXCEPT$
## Commercial License Usage
## Licensees holding valid commercial Qt licenses may use this file in
## accordance with the commercial license agreement provided with the
## Software or, alternatively, in accordance with the terms contained in
## a written agreement between you and The Qt Company. For licensing terms
## and conditions see https://www.qt.io/terms-conditions. For further
## information use the contact form at https://www.qt.io/contact-us.
##
## GNU General Public License Usage
## Alternatively, this file may be used under the terms of the GNU
## General Public License version 3 as published by the Free Software
## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
## included in the packaging of this file. Please review the following
## information to ensure the GNU General Public License requirements will
## be met: https://www.gnu.org/licenses/gpl-3.0.html.
##
## $QT_END_LICENSE$
##
#############################################################################

use strict;
use Cwd;
use warnings;

# Usage: test.pl <SearchPath> <ExecutionMode> <TestResults> <Timeout [Default 300 seconds]>
# Variable declarations to keep strict happy
our $SEARCH_PATH;
our $EXEC_MODE;
our $EXE_PREFIX;
our $EXE_SUFFIX;
our $TIMEOUT;
our $REPORTDIR;
our $buryChildren;
our $timeoutChildren;
our $totalExecuted;
our $totalStarted;
our $totalTimedOut;
our $currentDirectory;
our $testRoot;

# Where do we run this script? What directory?
$SEARCH_PATH=$ARGV[0];
if(!$SEARCH_PATH)
{
print "Please specify the search directory! \n";
exit(0);
}

# We have four options:
# 'U': Unix
# 'W': Windows
# 'M': Mac
# 'E': Embedded
$EXEC_MODE=$ARGV[1];
if($EXEC_MODE =~ /^U$/)
{
print "Using Unix execution mode\n";
$EXE_PREFIX="./";
$EXE_SUFFIX="";
} elsif($EXEC_MODE =~ /^W$/)
{
print "Using Windows execution mode\n";
$EXE_PREFIX="";
$EXE_SUFFIX=".exe";
} elsif($EXEC_MODE =~ /^M$/)
{
print "Using OSX execution mode\n";
$EXE_PREFIX="/Contents/MacOS/";
$EXE_SUFFIX=".app";
} elsif($EXEC_MODE =~ /^E$/)
{
print "Using embedded execution mode\n";
$EXE_PREFIX="./";
$EXE_SUFFIX="";
} else {
print "Unknown execution mode: $EXEC_MODE \n";
print "Use: 'U' (Unix), 'W' (Windows), 'M' (MacOSX)\n";
exit(0);
}
# We get the current directory, we 'll need it afterwards
$currentDirectory = getcwd();

$testRoot = Cwd::abs_path($SEARCH_PATH);

# We assume that by default goes to "reports" unless the user specifies it.
$REPORTDIR = $ARGV[2];
if(!$REPORTDIR)
{
$REPORTDIR = $testRoot."/reports";
mkdir $REPORTDIR;
} else {
mkdir $REPORTDIR;
$REPORTDIR = Cwd::abs_path($REPORTDIR);
}

# If given we use it, otherwise we default to 300 seconds.
$TIMEOUT = $ARGV[3];
if(!$TIMEOUT)
{
$TIMEOUT=300;
}
print "Timeout value: $TIMEOUT\n";

# Initialize 'global' variables
$buryChildren = 0;
$timeoutChildren = 0;
$totalExecuted = 0;
$totalStarted = 0;
$totalTimedOut = 0;

# Install signal handlers and pray for the best
$SIG{'CHLD'} = 'handleDeath';
$SIG{'ALRM'} = 'handleTimeout';

handleDir($testRoot);

print " ** Statistics ** \n";
print " Tests started: $totalStarted \n";
print " Tests executed: $totalExecuted \n";
print " Tests timed out: $totalTimedOut \n";

sub handleDir {

my ($dir) = @_;
my $currentDir = getcwd();

opendir(DIR, $dir);
my @files = readdir(DIR);
closedir DIR;
my $file;
foreach $file (@files) {
#skip hidden files
next if (substr($file,0,1) eq ".");

if ( -d $dir."/".$file) {
handleDir($dir."/".$file)
} elsif ( $file =~ /^tst_/ and -x $dir."/".$file ) {
chdir($dir) || die("Could not chdir to $dir");
executeTestCurrentDir($file);
chdir($currentDir);
}
}
}

sub executeTestCurrentDir {
my ($command) = @_;
print "Executing $command \n";
my $myPid;
$myPid = fork();
if($myPid == 0)
{
my $realCommand;
if($EXEC_MODE =~/^M$/)
{
$realCommand = "./".$command.$EXE_SUFFIX.$EXE_PREFIX.$command;
} else {
$realCommand = $EXE_PREFIX.$command.$EXE_SUFFIX;
}
my $outputRedirection = $REPORTDIR."/".$command.$EXE_SUFFIX.".xml";

if($EXEC_MODE =~ /^E$/)
{
exec($realCommand, "-qws", "-xml", "-o", $outputRedirection);
} else {
exec($realCommand, "-xml", "-o", $outputRedirection);
}
exit(0);
} elsif($myPid > 0 )
{
$totalStarted++;
alarm($TIMEOUT);
while(!$buryChildren && !$timeoutChildren)
{
sleep 10;
}
if($buryChildren)
{
my $value;
$value = waitpid($myPid , 0);
$buryChildren = 0;
$totalExecuted++;
} elsif($timeoutChildren)
{
kill 'INT', $myPid;
$timeoutChildren = 0;
$totalTimedOut++;
} else {
# What?? If we get here we need to abort, this is totally unexpected
print "Wrong condition evaluated, aborting to avoid damages\n";
exit(0);
}
# We need to handle children killed because of timeout
if($buryChildren)
{
my $value;
$value = waitpid($myPid , 0);
$buryChildren = 0;
}
} else {
print "Problems trying to execute $command";
}

}

# This procedure takes care of handling dead children on due time
sub handleDeath {
$buryChildren = 1;
}

# This takes care of timeouts
sub handleTimeout {
$timeoutChildren = 1;
}

目的

构建环境,学习 《Linux设备驱动程序》 《Linux内核设计与实现》

Linux环境

Fedora 24,内核版本4.5.5。

Linux 4.5.5-300.fc24.x86_64

下载内核源码

https://www.kernel.org下载了4.19.128版本。

内核配置

使用make help查看所有make选项。

可以使用不同的方法进行配置,比如make configmake menuconfigmake xconfig等。

阅读全文 »

《算法(第4版)》笔记

归并

归并排序基于“归并”这个简单的操作,即将两个有序的数组归并成一个更大的有序数组。

要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// c++
template<class T>
void merge(T& a, int lo, int mid, int hi, T& aux)
{
// 将a[lo .. mid]和a[mid+1 .. hi]归并
int i = lo;
int j = mid + 1;

for (int k=lo; k<=hi; ++k)
aux[k] = a[k];

for (int k=lo; k<=hi; ++k)
{
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (aux[j] < aux[i])
a[k] = aux[j++];
else
a[k] = aux[i++];
}
}
阅读全文 »

《算法(第4版)》笔记

选择排序

首先,找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。这种方法叫做选择排序,因为它在不断地选择剩余元素之中的最小者。

选择排序的缺点:一个已经有序的数组或是主键全部相等的数组和一个元素随机排列的数组所用的排序时间竟然一样长。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// c++
// 将容器a按升序排列
template<class T>
void selection_sort(T& a)
{
int n = a.size();
for (int i=0; i<n; ++i)
{
int min = i;
for (int j=i+1; j<n; ++j)
{
if (a[j] < a[min])
min = j;
}

std::swap(a[i], a[min]);
}
}
阅读全文 »

《SDL Game Development》笔记

坐标系
原点:左上角

SDL扩展
SDL_image
支持多种格式图片加载:BMP GIF PNG TGA PCX …
SDL_net
跨平台网络库
SDL_mixer
audio mixer library. 支持MP3 MIDI OGG
SDL_ttf
支持TrueType字体
SDL_rtf
support the rendering of the Rich Text Format (RTF).

SDL_Init()
初始化标识

1
2
3
4
5
6
7
SDL_INIT_HAPTIC      Force feedback subsystem  力反馈
SDL_INIT_AUDIO Audio subsystem
SDL_INIT_VIDEO Video subsystem
SDL_INIT_TIMER Timer subsystem
SDL_INIT_JOYSTICK Joystick subsystem
SDL_INIT_EVERYTHING All subsystems
SDL_INIT_noparachute Don't catch fatal signals

查看一个子系统是否已被初始化:

1
2
if (SDL_WasInit(SDL_INIT_VIDEO) != 0)
cout << "video was initialized";
1
2
3
4
5
SDL_CreateRenderer()
SDL_RENDERER_SOFTWARE Use software rendering
SDL_RENDERER_ACCELERATED Use hardware acceleration
SDL_RENDERER_PRESENTVSYNC Synchronize renderer update with screen's refresh rate
SDL_RENDERER_TARGETTEXTURE Supports render to texture

游戏程序结构
初始化
游戏循环:获取输入 物理运算 渲染
退出

window flags

1
2
3
4
5
6
7
8
9
10
11
12
SDL_WINDOW_FULLSCREEN      Make the window fullscreen
SDL_WINDOW_OPENGL Window can be used with as an OpenGL context
SDL_WINDOW_SHOWN The window is visible
SDL_WINDOW_HIDDEN Hide the window
SDL_WINDOW_BORDERLESS No border on the window
SDL_WINDOW_RESIZABLE Enable resizing of the window
SDL_WINDOW_MINIMIZED Minimize the window
SDL_WINDOW_MAXIMIZED Maximize the window
SDL_WINDOW_INPUT_GRABBED Window has grabbed input focus
SDL_WINDOW_INPUT_FOCUS Window has input focus
SDL_WINDOW_MOUSE_FOCUS Window has mouse focus
SDL_WINDOW_FOREIGN The window was not created using SDL

程序基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <SDL.h>

SDL_Window* g_pWindow = 0;
SDL_Renderer* g_pRenderer = 0;

int main(int argc, char* argv[])
{
// initialize SDL
if (SDL_Init(SDL_INIT_EVERYTHING) >= 0)
{
// if succeeded create our window
g_pWindow = SDL_CreateWindow("Chapter 1: Setting up SDL",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480,
SDL_WINDOW_SHOWN);

// if the window creation succeeded create our renderer
if (g_pWindow != 0)
{
g_pRenderer = SDL_CreateRenderer(g_pWindow, -1, 0);
}
}
else
return 1; // sdl could not initialize

// everything succeeded lets draw the window
bool bQuit = false;
while (true)
{
SDL_Event event;
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
bQuit = true;
break;
}
}
if (bQuit)
break;

// set to black
// This function expects Red, Green, Blue and Alpha as color values
SDL_SetRenderDrawColor(g_pRenderer, 0, 0, 0, 255);

// clear the window to black
SDL_RenderClear(g_pRenderer);

// show the window
SDL_RenderPresent(g_pRenderer);
}

// set a delay before quitting
//SDL_Delay(5000);

// clean up SDL
SDL_Quit();

return 0;
}

绘制图片过程

  1. 加载图片获得SDL_Surface
  2. 根据Surface获得SDL_Texture
  3. 获得纹理尺寸:SDL_QueryTexture
  4. 渲染:SDL_RenderCopy/SDL_RenderCopyEx 需要Renderer参数

SDL使用两种数据结构渲染到屏幕
SDL_Surface 像素集 使用软件渲染(not GPU)
SDL_Texture 使用硬件加速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SDL_Texture* m_pTexture
SDL_Rect m_sourceRectangle;
SDL_Rect m_destinationRectangle;

// 根据图片创建SDL_Texture
SDL_Surface* pTempSurface = SDL_LoadBMP("asserts/rider.bmp");
m_pTexture = SDL_CreateTextureFromSurface(m_pRenderer, pTempSurface);
SDL_FreeSurface(pTempSurface);

// 获得图片的大小
SDL_QueryTexture(m_pTexture, NULL, NULL,
&m_sourceRectangle.w, &m_sourceRectangle.h);

SDL_RenderClear();
SDL_RenderCopy(m_pRenderer, m_pTexture,
&m_sourceRectangle, &m_destinationRectangle);
SDL_RenderPresent();

源矩形 目标矩形 参数传入0 将整个纹理渲染到整个窗口。

SDL_GetTicks() 毫秒

SDL_RenderCopyEx() 支持旋转和翻转Flip

SDL_image
sdl2_image.lib

1
2
#include <sdl_image.h>
SDL_Surface* pTempSurface = IMG_Load("animate.png");

Fixed frames per second (FPS) is
not necessarily always a good option, especially when your game includes more
advanced physics. It is worth bearing this in mind when you move on from this
book and start developing your own games. Fixed FPS will, however, be fine for
the small 2D games, which we will work towards in this book.

固定帧频

1
2
3
4
5
6
7
8
9
10
const int FPS = 60;
const int DELAY_TIME = 1000.0f / FPS; // 1000毫秒
while (...)
{
frameStart = SDL_GetTicks(); // 毫秒
...
frameTime = SDL_GetTicks() - frameStart;
if (frameTime < DELAY_TIME)
SDL_Delay((int)(DELAY_TIME - frameTime));
}
1
2
3
4
5
6
7
8
9
10
11
12
SDL joystick event
SDL_JoyAxisEvent Axis motion information
SDL_JoyButtonEvent Button press and release information
SDL_JoyBallEvent Trackball event motion information
SDL_JoyHatEvent Joystick hat position change

SDL joystick event Type value
SDL_JoyAxisEvent SDL_JOYAXISMOTION
SDL_JoyButtonEvent SDL_JOYBUTTONDOWN or
SDL_JOYBUTTONUP
SDL_JoyBallEvent SDL_JOYBALLMOTION
SDL_JoyHatEvent SDL_JOYHATMOTION

不同的游戏控制器 按钮和轴可能有不同的值 比如Xbox360 controller, PS3 controller
Xbox360 controller:
Two analog sticks
Analog sticks press as buttons
Start and Select buttons
Four face buttons: A, B, X, and Y
Four triggers: two digital and two analog
A digital directional pad

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
if (SDL_NumJoysticks() > 0)
{
for (int i=0; i<SDL_NumJoysticks(); ++i)
{
SDL_Joystick* joy = SDL_JoystickOpen(i);
if (SDL_JoystickOpened(i) == 1)
m_joysticks.push_back(joy);
}
SDL_JoystickEventState(SDL_ENABLE);
}

SDL_JoystickClose(joy);

分辨是哪个控制器的事件

1
2
if (event.type == SDL_JOYAXISMOTION)
int whichOne = event.jaxis.which;

控制器按钮
SDL_JoystickNumButtons
event.jbutton.button // 按钮ID

鼠标事件

1
2
3
4
5
6
7
8
9
SDL Mouse Event        Purpose
SDL_MouseButtonEvent A button on the mouse has been pressed or released
SDL_MouseMotionEvent The mouse has been moved
SDL_MouseWheelEvent The mouse wheel has moved

SDL Mouse Event Type Value
SDL_MouseButtonEvent SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP
SDL_MouseMotionEvent SDL_MOUSEMOTION
SDL_MouseWheelEvent SDL_MOUSEWHEEL
1
2
3
4
5
// 鼠标按钮
// SDL numbers these as 0 for left, 1 for middle, and 2 for right.

if (event.type == SDL_MOUSEBUTTONDOWN)
if (event.button.button == SDL_BUTTON_LEFT)

event.type SDL_MOUSEMOTION
event.motion.x
event.motion.y

// 键盘
1 表示按下 0 表示没有按下
SDL_GetKeyboardState(int* numkeys)
Uint8* m_keystates;
m_keystates = SDL_GetKeyboardState(0);
SDL_Scancode key;
if (m_keysttes[key] == 1)

有限状态机
需要能够处理以下情况:
Removing one state and adding another
Adding one state without removing the previous state
Removing one state without adding another

Game
GameObject
TextureManager // 负责加载图片文件,负责绘制,纹理ID
InputHandler
GameState
GameStateMachine
GameObjectFactory
StateParser

Distributed Factory
class GameObjectFactory
std::map<std::string, BaseCreator*> m_creators;
registerType(std::string typeID, BaseCreator* pCreator);

https://github.com/ReneNyffenegger/development_misc/tree/master/base64.
The base64.h and base64.cpp files can be added directly to the project.

1
2
3
4
5
6
7
8
9
10
11
12
SDL_Mixer
Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize)
Mix_OpenAudio(22050, AUDIO_S16, 2, 4096);
std::map<std::string, Mix_Chunk*> mSfxs;
std::map<std::string, Mix_Chunk*> mMusic;

Mix_Music* music = Mix_LoadMUS(filename); // ogg
Mix_Chunk* chunk = Mix_LoadWAV(filename); // wav

Mix_PlayMusic() // Mix_Music
Mix_PlayChannel() // Mix_Chunk
Mix_CloseAudio();

SDL_SetTextureAlphaMod()

开发环境配置

http://tjumyk.github.io/sdl-tutorial-cn/contents.html

VS 新建项目选择控制台程序,链接属性中选择子系统“/SUBSYSTEM:WINDOWS”。

在屏幕上显示一张图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <SDL/SDL.h>

int main(int argc, char *args[])
{
SDL_Surface *hello = nullptr;
SDL_Surface *screen = nullptr;

SDL_Init(SDL_INIT_EVERYTHING);

// 设置窗口
screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

// 加载图像
hello = SDL_LoadBMP("hello.bmp");

// 将图像应用到窗口上
SDL_BlitSurface(hello, nullptr, screen, nullptr);

// 更新窗口
SDL_Flip(screen);

SDL_Delay(2000);

// 释放资源
SDL_FreeSurface(hello);
SDL_Quit();
return 0;
}
阅读全文 »

以下摘自《敏捷软件开发:原则、模式与实践》。

可持续的开发速度:

软件项目不是全速短跑,它是马拉松长跑。那些一跃过起跑线就开始尽力狂奔的团队将会在远离终点前就筋疲力尽。为了快速地完成开发,团队必须要以一种可持续的速度前进。团队必须保持旺盛的精力和敏锐的警觉。团队必须要有意识地保持稳定、适中的速度。

XP(极限编程)的规则不允许团队加班工作。在版本发布前的一个星期是该规则的唯一例外。如果发布目标就在眼前并且能够一蹴而就,则允许加班。

0%