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 ¬ification)
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