1 /*
2  * %kadu copyright begin%
3  * Copyright 2011 Tomasz Rostanski (rozteck@interia.pl)
4  * Copyright 2012 Wojciech Treter (juzefwt@gmail.com)
5  * Copyright 2012, 2013, 2014 Bartosz Brachaczek (b.brachaczek@gmail.com)
6  * Copyright 2011, 2013, 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
7  * %kadu copyright end%
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "pcspeaker-notifier.h"
24 
25 #include "pcspeaker-configuration-widget.h"
26 
27 #include "configuration/configuration.h"
28 #include "configuration/deprecated-configuration-api.h"
29 #include "notification/notification.h"
30 #include "plugin/plugin-injected-factory.h"
31 
32 #ifdef Q_OS_WIN
33 #include <windows.h>
34 #endif
35 
36 #if defined(Q_OS_UNIX)
37 #include <X11/Xlib.h>
38 #include <X11/keysym.h>
39 #include <unistd.h>
40 #endif
41 
42 //Sound Frequencies
43 //Rows - sounds: C, C#, D, D#, E, F, F#, G, G#, A, A#, B
44 //Cols - octaves (0 to 7)
45 int sounds[96] = {
46 	16,33, 65,131,262,523,1046,2093,
47 	17,35, 69,139,277,554,1109,2217,
48 	18,37, 73,147,294,587,1175,2349,
49 	19,39, 78,155,311,622,1244,2489,
50 	21,41, 82,165,330,659,1328,2637,
51 	22,44, 87,175,349,698,1397,2794,
52 	23,46, 92,185,370,740,1480,2960,
53 	24,49, 98,196,392,784,1568,3136,
54 	26,52,104,208,415,831,1661,3322,
55 	27,55,110,220,440,880,1760,3520,
56 	29,58,116,233,466,932,1865,3729,
57 	31,62,123,245,494,988,1975,3951};
58 
59 #if defined(Q_OS_WIN)
beep(int pitch,int duration)60 void PCSpeakerNotifier::beep(int pitch, int duration)
61 {
62 	if (pitch == 0)
63 		Sleep(duration / 5); /* instead of (duration * 200) / 1000 */
64 	else
65 		Beep(pitch, duration);
66 }
67 #elif defined(Q_OS_UNIX)
beep(int pitch,int duration)68 void PCSpeakerNotifier::beep(int pitch, int duration)
69 {
70 	if (pitch == 0)
71 		usleep(static_cast<useconds_t>(duration * 200));
72 	else
73 	{
74 		XKeyboardState s;			//save previous sound config
75 		XGetKeyboardControl(xdisplay, &s);
76 		XKeyboardControl v;			//pause when set to 0
77 		v.bell_pitch = pitch;			//sound frequency in Hz
78 		v.bell_duration = duration;		//sound duration
79 		v.bell_percent = 100;			//set volume to max
80 		XChangeKeyboardControl(xdisplay, (KBBellPitch | KBBellDuration | KBBellPercent), &v); //set sound config
81 		XBell(xdisplay, volume);  		//put sound to buffer
82 		XFlush(xdisplay);			//flush buffer (beep)
83 		usleep(static_cast<useconds_t>(pitch * 100));			//wait until sound is played
84 		v.bell_pitch = static_cast<int>(s.bell_pitch);		//restore previous sound config
85 		v.bell_duration = static_cast<int>(s.bell_duration);
86 		v.bell_percent = static_cast<int>(s.bell_percent);
87 		XChangeKeyboardControl(xdisplay, (KBBellPitch | KBBellDuration | KBBellPercent), &v); //set restored sound config
88 	}
89 }
90 #else
beep(int pitch,int duration)91 void PCSpeakerNotifier::beep(int pitch, int duration)
92 {
93 	Q_UNUSED(pitch);
94 	Q_UNUSED(duration);
95 }
96 #endif
97 
PCSpeakerNotifier(QObject * parent)98 PCSpeakerNotifier::PCSpeakerNotifier(QObject *parent) :
99 		QObject{parent},
100 		Notifier{"PC Speaker", QT_TRANSLATE_NOOP("@default", "PC Speaker"), KaduIcon("audio-volume-low")},
101 #if defined(Q_OS_UNIX)
102 		xdisplay{},
103 #endif
104 		volume{}
105 {
106 }
107 
~PCSpeakerNotifier()108 PCSpeakerNotifier::~PCSpeakerNotifier()
109 {
110 }
111 
setConfiguration(Configuration * configuration)112 void PCSpeakerNotifier::setConfiguration(Configuration *configuration)
113 {
114 	m_configuration = configuration;
115 }
116 
setPluginInjectedFactory(PluginInjectedFactory * pluginInjectedFactory)117 void PCSpeakerNotifier::setPluginInjectedFactory(PluginInjectedFactory *pluginInjectedFactory)
118 {
119 	m_pluginInjectedFactory = pluginInjectedFactory;
120 }
121 
createConfigurationWidget(QWidget * parent)122 NotifierConfigurationWidget * PCSpeakerNotifier::createConfigurationWidget(QWidget *parent)
123 {
124 	return m_pluginInjectedFactory->makeInjected<PCSpeakerConfigurationWidget>(this, parent);
125 }
126 
notify(const Notification & notification)127 void PCSpeakerNotifier::notify(const Notification &notification)
128 {
129 	parseAndPlay(m_configuration->deprecatedApi()->readEntry("PC Speaker", notification.type + "_Sound"));
130 }
131 
parseStringToSound(QString line,int tab[21],int tab2[21])132 void PCSpeakerNotifier::parseStringToSound(QString line, int tab[21], int tab2[21])
133 {
134 	int length = line.length();
135 	line = line.toUpper();
136 	int tmp, k = 0;
137 	char znak, tmp3;
138 	int i;
139 	if (length > 0)
140 	{
141 		for (i=0; i<length; ++i)					//for each sound
142 		{
143 			if (k >= 20) break;
144 			znak=line[i].toLatin1();
145 			switch (znak) {						//calculate offset in sound table
146 				case 'C':  tmp=0;	break;
147 				case 'D':  tmp=2;	break;
148 				case 'E':  tmp=4;	break;
149 				case 'F':  tmp=5;	break;
150 				case 'G':  tmp=7;	break;
151 				case 'A':  tmp=9;	break;
152 				case 'B':  tmp=11;	break;
153 				case '_':
154 					{
155 						tab[k] = 0; 			//play pause
156 						tmp=-1;
157 						if (line[i+1]=='/')		//set pause length
158 						{
159 							if (line[i+2]=='F') tmp3=16;
160 							else if ((line[i+2]>='1') && (line[i+2]<='8'))
161 								tmp3=line[i+2].toLatin1()-48;
162 							else tmp3=1;
163 								tab2[k]=(1000/tmp3);
164 							i+=2;
165 						}
166 						else tab2[k]=1000;		//if not given use 1000
167 							++k;
168 					}
169 					break;
170 					default: tmp=-1;
171 			}
172 			if (tmp>=0) {
173 				tmp*=8;
174 				bool alreadyHalf = false;
175 				if (line[i+1]=='#')
176 				{						//for halftone
177 					tmp+=8;					//set offset
178 					++i;					//go forward
179 					alreadyHalf = true;
180 				}
181 				if ((line[i+1]>='0') && (line[i+1]<='7'))
182 				{
183 					tmp+=line[i+1].toLatin1()-48;		//calculate offset basing on octave
184 					++i;					//go forward
185 				}
186 				if (line[i+1]=='#')
187 				{						//for halftone
188 					if (!alreadyHalf)
189 						tmp+=8;					//set offset
190 					++i;					//go forward
191 				}
192 
193 				if (tmp >= 0 && tmp < 96)
194 				{
195 					tab[k]=sounds[tmp];				//store sound frequency
196 					if (line[i+1]=='/')
197 					{
198 							//set duration
199 							if (line[i+2]=='F') tmp3=16;
200 							else if ((line[i+2]>='1') && (line[i+2]<='8')) tmp3=line[i+2].toLatin1()-48;
201 							else tmp3=1;
202 							tab2[k]=(1000/tmp3);
203 							i+=2;
204 					}
205 					else tab2[k]=1000;				//if not given use 1000
206 					++k;						//move to the next sound
207 				}
208 			}
209 		}
210 	}
211 	tab[k]=-1;								//set sound end condition (-1)
212 }
213 
play(int sound[21],int soundlength[20])214 void PCSpeakerNotifier::play(int sound[21], int soundlength[20])
215 {
216 #if defined(Q_OS_UNIX)
217 	xdisplay = XOpenDisplay(NULL);
218 #endif
219 	for (int i=0; i<20; ++i)
220 	{
221 		if (sound[i] == -1) break;
222 		beep(sound[i], soundlength[i]);
223 	}
224 #if defined(Q_OS_UNIX)
225 	XCloseDisplay(xdisplay);
226 #endif
227 }
228 
parseAndPlay(QString line)229 void PCSpeakerNotifier::parseAndPlay(QString line)
230 {
231 	volume = m_configuration->deprecatedApi()->readNumEntry("PC Speaker", "SpeakerVolume");
232 	int sound[21], soundLength[20];
233 	parseStringToSound(line, sound, soundLength);
234 	play(sound, soundLength);
235 }
236 
createDefaultConfiguration()237 void PCSpeakerNotifier::createDefaultConfiguration()
238 {
239 	m_configuration->deprecatedApi()->addVariable("PC Speaker", "SpeakerVolume", 100);
240 	m_configuration->deprecatedApi()->addVariable("PC Speaker", "NewChat_Sound", "C4/2");
241 	m_configuration->deprecatedApi()->addVariable("PC Speaker", "NewMessage_Sound", "F2/2");
242 	m_configuration->deprecatedApi()->addVariable("PC Speaker", "ConnectionError_Sound", "D3/4");
243 	m_configuration->deprecatedApi()->addVariable("PC Speaker", "StatusChanged_Sound", "A3/2");
244 	m_configuration->deprecatedApi()->addVariable("PC Speaker", "FileTransfer_Sound", "E4/4");
245 }
246 
247 #include "moc_pcspeaker-notifier.cpp"
248